Plugin Directory

Changeset 3264602


Ignore:
Timestamp:
03/31/2025 04:55:15 PM (11 months ago)
Author:
logtivity
Message:

Fix for new capabilities and improved input sanitization

Location:
logtivity
Files:
20 edited
1 copied

Legend:

Unmodified
Added
Removed
  • logtivity/tags/3.1.7/Admin/Logtivity_Admin.php

    r3251888 r3264602  
    102102    public function registerOptionsPage()
    103103    {
    104         Logtivity::checkCapabilities();
    105 
    106104        if (!apply_filters('logtivity_hide_from_menu', false)) {
    107105            add_menu_page(
  • logtivity/tags/3.1.7/Errors/Logtivity_Error_Logger.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528class Logtivity_Error_Logger extends Logtivity_Api
    2629{
    27     use Logtivity_User_Logger_Trait;
     30    use Logtivity_User_Logger_Trait;
    2831
    29     protected $active = true;
     32    /**
     33     * @var bool
     34     */
     35    protected bool $active = true;
    3036
    31     protected $error;
     37    /**
     38     * @var array
     39     */
     40    protected array $error = [];
    3241
    33     protected static $recordedErrors = [];
     42    /**
     43     * @var string[]
     44     */
     45    protected static array $recordedErrors = [];
    3446
    35     public function __construct($error)
    36     {
    37         $this->error = $error;
     47    /**
     48     * @param array $error
     49     */
     50    public function __construct(array $error)
     51    {
     52        $this->error = $error;
    3853
    39         $this->setUser();
     54        $this->setUser();
    4055
    41         parent::__construct();
    42     }
     56        parent::__construct();
     57    }
    4358
    44     public function stop()
    45     {
    46         $this->active = false;
     59    /**
     60     * @return $this
     61     */
     62    public function stop(): self
     63    {
     64        $this->active = false;
    4765
    48         return $this;
    49     }
     66        return $this;
     67    }
    5068
    51     public function send()
    52     {
    53         if (in_array($this->error['message'], self::$recordedErrors)) {
    54             return;
    55         }
     69    /**
     70     * @return ?string
     71     */
     72    public function send(): ?string
     73    {
     74        $message = $this->error['message'] ?? '';
     75        if (in_array($message, self::$recordedErrors) === false) {
     76            self::$recordedErrors[] = $this->error['message'];
    5677
    57         self::$recordedErrors[] = $this->error['message'];
     78            do_action('wp_logtivity_error_logger_instance', $this);
    5879
    59         do_action('wp_logtivity_error_logger_instance', $this);
     80            if ($this->active) {
     81                $response = $this->async()->makeRequest('/errors/store', $this->getData());
     82            }
     83        }
    6084
    61         if (!$this->active) {
    62             return;
    63         }
     85        return $response ?? null;
     86    }
    6487
    65         return $this->async()
    66                     ->makeRequest(
    67                         '/errors/store',
    68                         $this->getData()
    69                     );
    70     }
     88    /**
     89     * @return array
     90     */
     91    public function getData(): array
     92    {
     93        $error = explode('Stack trace:', $this->error['message']);
    7194
    72     public function getData()
    73     {
    74         $error = explode('Stack trace:', $this->error['message']);
     95        return [
     96            'type'               => $this->getErrorLevel($this->error['type']) ?? null,
     97            'message'            => $error[0],
     98            'stack_trace'        => $this->generateStackTrace(
     99                [
     100                    'file' => $this->error['file'] ?? null,
     101                    'line' => $this->error['line'] ?? null,
     102                ],
     103                $error[1] ?? null
     104            ),
     105            'file'               => $this->error['file'] ?? null,
     106            'line'               => $this->error['line'] ?? null,
     107            'user_id'            => $this->getUserID(),
     108            'username'           => $this->maybeGetUsersUsername(),
     109            'ip_address'         => $this->maybeGetUsersIp(),
     110            'user_authenticated' => $this->user->isLoggedIn(),
     111            'url'                => $this->getCurrentUrl(),
     112            'method'             => $this->getRequestMethod(),
     113            'php_version'        => phpversion(),
     114            'level'              => $this->error['level'] ?? null,
     115        ];
     116    }
    75117
    76         return [
    77             'type' => $this->getErrorLevel($this->error['type']) ?? null,
    78             'message' => $error[0],
    79             'stack_trace' => $this->generateStackTrace(
    80                 [
    81                     'file' => $this->error['file'] ?? null,
    82                     'line' => $this->error['line'] ?? null,
    83                 ],
    84                 $error[1] ?? null
    85             ),
    86             'file' => $this->error['file'] ?? null,
    87             'line' => $this->error['line'] ?? null,
    88             'user_id' => $this->getUserID(),
    89             'username' => $this->maybeGetUsersUsername(),
    90             'ip_address' => $this->maybeGetUsersIp(),
    91             'user_authenticated' => $this->user->isLoggedIn(),
    92             'url' => $this->getCurrentUrl(),
    93             'method' => $this->getRequestMethod(),
    94             'php_version' => phpversion(),
    95             'level' => $this->error['level'] ?? null,
    96         ];
    97     }
     118    /**
     119     * @param array   $line
     120     * @param ?string $stackTrace
     121     *
     122     * @return array
     123     */
     124    private function generateStackTrace(array $line, ?string $stackTrace): array
     125    {
     126        $stackTraceObject = new Logtivity_Stack_Trace();
    98127
    99     private function generateStackTrace($line, $stackTrace)
    100     {
    101         $stackTraceObject = new Logtivity_Stack_Trace();
     128        if (isset($this->error['stack_trace'])) {
     129            return $stackTraceObject->createFromArray($this->error['stack_trace']);
     130        }
    102131
    103         if (isset($this->error['stack_trace'])) {
    104             return $stackTraceObject->createFromArray($this->error['stack_trace']);
    105         }
     132        return array_merge(
     133            [$stackTraceObject->createFileObject($line['file'], $line['line'])],
     134            $stackTraceObject->createFromString($stackTrace)
     135        );
     136    }
    106137
    107         return array_merge(
    108             [$stackTraceObject->createFileObject($line['file'], $line['line'])],
    109             $stackTraceObject->createFromString($stackTrace)
    110         );
    111     }
     138    /**
     139     * @return string
     140     */
     141    private function getRequestMethod(): string
     142    {
     143        return sanitize_text_field($_SERVER['REQUEST_METHOD'] ?? '');
     144    }
    112145
    113     private function getRequestMethod()
    114     {
    115         return $_SERVER['REQUEST_METHOD'] ?? null;
    116     }
     146    /**
     147     * @return ?string
     148     */
     149    private function getCurrentUrl(): ?string
     150    {
     151        $host = sanitize_text_field($_SERVER['HTTP_HOST'] ?? null);
     152        if ($host) {
     153            $ssl = sanitize_text_field($_SERVER['HTTPS'] ?? 'off') != 'off'
     154                || sanitize_text_field($_SERVER['SERVER_PORT'] ?? '80') == 443;
    117155
    118     private function getCurrentUrl()
    119     {
    120         if (!isset($_SERVER['HTTP_HOST'])) {
    121             return;
    122         }
     156            $protocol = $ssl ? 'https://' : 'http://';
    123157
    124         $protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
     158            $path = sanitize_text_field($_SERVER['REQUEST_URI'] ?? '');
    125159
    126         $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    127        
    128         if ($url == $protocol) {
    129             return;
    130         }
     160            $url = sanitize_url($protocol . $host . $path);
    131161
    132         return $url;
    133     }
     162            if ($url != $protocol) {
     163                return $url;
     164            }
     165        }
    134166
    135     private function getErrorLevel($level)
    136     {
    137         $errorlevels = logtivity_get_error_levels();
     167        return null;
     168    }
    138169
    139         return isset($errorlevels[$level]) ? $errorlevels[$level] : $level;
    140     }
     170    /**
     171     * @param $level
     172     *
     173     * @return string
     174     */
     175    private function getErrorLevel($level): string
     176    {
     177        $errorLevels = logtivity_get_error_levels();
     178
     179        return $errorLevels[$level] ?? $level;
     180    }
    141181}
  • logtivity/tags/3.1.7/Helpers/Helpers.php

    r3246625 r3264602  
    120120    $hash = (new Logtivity_Options())->urlHash();
    121121
    122     if (!$hash) {
    123         return false;
    124     }
    125 
    126     return $hash !== md5(home_url());
     122    return $hash != md5(home_url());
    127123}
    128124
     
    145141    return $errorLevels;
    146142}
     143
     144/**
     145 * Get all known capabilities
     146 *
     147 * @return array
     148 */
     149function logtivity_get_capabilities(): array
     150{
     151    global $wp_roles;
     152
     153    $capabilities = [];
     154    foreach ($wp_roles->roles as $key => $role ) {
     155        $capabilities = array_merge($capabilities, $role['capabilities']);
     156    }
     157
     158    return $capabilities;
     159}
  • logtivity/tags/3.1.7/Logs/Core/Logtivity_Post.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
     26// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     27// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     28
    2529class Logtivity_Post extends Logtivity_Abstract_Logger
    2630{
    27     protected $disableUpdateLog = false;
    28    
    29     protected $action;
    30 
    31     public function registerHooks()
    32     {
    33         add_action( 'transition_post_status', [$this, 'postStatusChanged'], 10, 3);
    34         add_action( 'save_post', array( $this, 'postWasUpdated' ), 10, 3 );
    35         add_action( 'wp_trash_post', [$this, 'postWasTrashed'], 10, 1 );
    36         add_filter('wp_handle_upload', [$this, 'mediaUploaded'], 10, 2);
    37         add_action('delete_post', [$this, 'postPermanentlyDeleted'], 10, 1);
    38         add_filter( 'wp_ajax_save-attachment', [$this, 'mediaMetaUpdated'], -1 );
    39     }
    40 
    41     public function postStatusChanged($new_status, $old_status, $post)
    42     {
    43         if ($this->shouldIgnore($post)) {
    44             return;
    45         }
    46 
    47         if ($old_status == 'trash') {
    48             $this->disableUpdateLog = true;
    49             return $this->postWasRestored($post);
    50         }
    51 
    52         if ($old_status != 'publish' && $new_status == 'publish') {
    53             $this->action = $this->getPostTypeLabel($post->ID) . ' Published';
    54             return;
    55         }
    56 
    57         if ($old_status == 'publish' && $new_status == 'draft') {
    58             $this->action = $this->getPostTypeLabel($post->ID) . ' Unpublished';
    59             return;
    60         }
    61 
    62         if ($old_status != $new_status) {
    63             Logtivity_Logger::log()
    64                 ->setAction(
    65                     $this->getPostTypeLabel($post->ID) . ' Status changed from '.$old_status.' to '.$new_status
    66                 )
    67                 ->setContext($post->post_title)
    68                 ->setPostType($post->post_type)
    69                 ->setPostId($post->ID)
    70                 ->send();
    71         }
    72 
    73     }
    74 
    75     /**
    76      * Post was updated or created. ignoring certain auto save system actions
    77      *
    78      * @param  integer $post_id
    79      * @param  WP_Post $post   
    80      * @param  bool $update
    81      * @return void
    82      */
    83     public function postWasUpdated($post_id, $post, $update)
    84     {
    85         if ($this->disableUpdateLog) {
    86             return;
    87         }
    88 
    89         if ($this->shouldIgnore($post)) {
    90             return;
    91         }
    92 
    93         if ($this->loggedRecently($post->ID)) {
    94             return true;
    95         }
    96 
    97         $revision = $this->getRevision($post_id);
    98 
    99         Logtivity_Logger::log()
    100             ->setAction($this->action ?? $this->getPostTypeLabel($post->ID) . ' Updated')
    101             ->setContext($post->post_title)
    102             ->setPostType($post->post_type)
    103             ->setPostId($post->ID)
    104             ->addMeta('Post Title', $post->post_title)
    105             ->addMeta('Post Status', $post->post_status)
    106             ->addMetaIf($revision, 'View Revision', $revision)
    107             ->send();
    108 
    109         update_post_meta($post_id, 'logtivity_last_logged', (new \DateTime())->format('Y-m-d H:i:s'));
    110     }
    111 
    112     private function getRevision( $post_id )
    113     {
    114         $revisions = wp_get_post_revisions( $post_id );
    115         if ( ! empty( $revisions ) ) {
    116             $revision = array_shift( $revisions );
    117             return $this->getRevisionLink( $revision->ID );
    118         }
    119     }
    120 
    121     private function getRevisionLink( $revision_id )
    122     {
    123         return ! empty( $revision_id ) ? add_query_arg( 'revision', $revision_id, admin_url( 'revision.php' ) ) : null;
    124     }
    125 
    126     public function postWasTrashed($post_id)
    127     {
    128         if (get_post_type($post_id) == 'customize_changeset') {
    129             return;
    130         }
    131        
    132         return Logtivity_Logger::log()
    133             ->setAction($this->getPostTypeLabel($post_id) . ' Trashed')
    134             ->setContext(logtivity_get_the_title($post_id))
    135             ->setPostType(get_post_type($post_id))
    136             ->setPostId($post_id)
    137             ->addMeta('Post Title', logtivity_get_the_title($post_id))
    138             ->send();
    139     }
    140 
    141     public function postWasRestored($post)
    142     {
    143         return Logtivity_Logger::log()
    144             ->setAction(
    145                 $this->getPostTypeLabel($post->ID) . ' Restored from Trash'
    146             )
    147             ->setContext($post->post_title)
    148             ->setPostType($post->post_type)
    149             ->setPostId($post->ID)
    150             ->addMeta('Post Title', $post->post_title)
    151             ->send();
    152     }
    153 
    154     public function postPermanentlyDeleted($post_id)
    155     {
    156         if ($this->ignoringPostType(get_post_type($post_id))) {
    157             return;
    158         }
    159 
    160         if ($this->ignoringPostTitle(logtivity_get_the_title($post_id))) {
    161             return;
    162         }
    163 
    164         return Logtivity_Logger::log()
    165             ->setAction(
    166                 $this->getPostTypeLabel($post_id) . ' Permanently Deleted'
    167             )
    168             ->setContext(logtivity_get_the_title($post_id))
    169             ->setPostType(get_post_type($post_id))
    170             ->setPostId($post_id)
    171             ->addMeta('Post Title', logtivity_get_the_title($post_id))
    172             ->send();
    173     }
    174 
    175     public function mediaUploaded($upload, $context)
    176     {
    177         Logtivity_Logger::log()
    178             ->setAction('Attachment Uploaded')
    179             ->setContext(basename($upload['file']))
    180             ->addMeta('Url', $upload['url'])
    181             ->addMeta('Type', $upload['type'])
    182             ->addMeta('Context', $context)
    183             ->send();
    184 
    185         return $upload;
    186     }
    187 
    188     public function mediaMetaUpdated()
    189     {
    190         $post_id = absint($_POST['id']);
    191 
    192         if ($post_id) {
    193             Logtivity_Logger::log()
    194                 ->setAction('Attachment Meta Updated.')
    195                 ->addMeta("Media ID", $post_id)
    196                 ->addMeta("Changes", ( isset($_POST['changes']) ? $_POST['changes'] : null))
    197                 ->send();
    198         }
    199 
    200         return $post;
    201     }
     31    /**
     32     * @var bool
     33     */
     34    protected bool $disableUpdateLog = false;
     35
     36    /**
     37     * @var ?string
     38     */
     39    protected ?string $action = null;
     40
     41    /**
     42     * @return void
     43     */
     44    public function registerHooks(): void
     45    {
     46        add_action('transition_post_status', [$this, 'postStatusChanged'], 10, 3);
     47        add_action('save_post', [$this, 'postWasUpdated'], 10, 3);
     48        add_action('wp_trash_post', [$this, 'postWasTrashed']);
     49        add_action('delete_post', [$this, 'postPermanentlyDeleted']);
     50
     51        add_filter('wp_handle_upload', [$this, 'mediaUploaded'], 10, 2);
     52    }
     53
     54    /**
     55     * @param string  $newStatus
     56     * @param string  $oldStatus
     57     * @param WP_Post $post
     58     *
     59     * @return void
     60     */
     61    public function postStatusChanged(string $newStatus, string $oldStatus, WP_Post $post): void
     62    {
     63        if ($this->shouldIgnore($post) == false) {
     64            if ($oldStatus == 'trash') {
     65                $this->disableUpdateLog = true;
     66                $this->postWasRestored($post);
     67
     68            } elseif ($oldStatus != 'publish' && $newStatus == 'publish') {
     69                $this->action = $this->getPostTypeLabel($post->ID) . ' Published';
     70
     71            } elseif ($oldStatus == 'publish' && $newStatus == 'draft') {
     72                $this->action = $this->getPostTypeLabel($post->ID) . ' Unpublished';
     73
     74            } elseif ($oldStatus != $newStatus) {
     75                $action = sprintf(
     76                    '%s Status changed from %s to %s',
     77                    $this->getPostTypeLabel($post->ID),
     78                    $oldStatus,
     79                    $newStatus
     80                );
     81
     82                Logtivity_Logger::log()
     83                    ->setAction($action)
     84                    ->setContext($post->post_title)
     85                    ->setPostType($post->post_type)
     86                    ->setPostId($post->ID)
     87                    ->send();
     88            }
     89        }
     90    }
     91
     92    /**
     93     * Post was updated or created. ignoring certain auto save system actions
     94     *
     95     * @param int     $postId
     96     * @param WP_Post $post
     97     *
     98     * @return void
     99     */
     100    public function postWasUpdated(int $postId, WP_Post $post): void
     101    {
     102        if (
     103            $this->disableUpdateLog == false
     104            && $this->shouldIgnore($post) == false
     105            && $this->loggedRecently($post->ID) == false
     106        ) {
     107            $revision = $this->getRevision($postId);
     108
     109            Logtivity_Logger::log()
     110                ->setAction($this->action ?: $this->getPostTypeLabel($post->ID) . ' Updated')
     111                ->setContext($post->post_title)
     112                ->setPostType($post->post_type)
     113                ->setPostId($post->ID)
     114                ->addMeta('Post Title', $post->post_title)
     115                ->addMeta('Post Status', $post->post_status)
     116                ->addMetaIf($revision, 'View Revision', $revision)
     117                ->send();
     118
     119            update_post_meta($postId, 'logtivity_last_logged', (new DateTime())->format('Y-m-d H:i:s'));
     120        }
     121    }
     122
     123    /**
     124     * @param int $postId
     125     *
     126     * @return ?string
     127     */
     128    protected function getRevision(int $postId): ?string
     129    {
     130        if ($revisions = wp_get_post_revisions($postId)) {
     131            $revision = array_shift($revisions);
     132
     133            $revision = $this->getRevisionLink($revision->ID);
     134        }
     135
     136        return $revision ?? null;
     137    }
     138
     139    /**
     140     * @param null|int $revisionId
     141     * @return null|string
     142     */
     143    private function getRevisionLink(?int $revisionId): ?string
     144    {
     145        return $revisionId
     146            ? add_query_arg('revision', $revisionId, admin_url('revision.php'))
     147            : null;
     148    }
     149
     150    /**
     151     * @param int $postId
     152     *
     153     * @return void
     154     */
     155    public function postWasTrashed(int $postId): void
     156    {
     157        if (get_post_type($postId) != 'customize_changeset') {
     158            Logtivity_Logger::log()
     159                ->setAction($this->getPostTypeLabel($postId) . ' Trashed')
     160                ->setContext(logtivity_get_the_title($postId))
     161                ->setPostType(get_post_type($postId))
     162                ->setPostId($postId)
     163                ->addMeta('Post Title', logtivity_get_the_title($postId))
     164                ->send();
     165        }
     166    }
     167
     168    /**
     169     * @param WP_Post $post
     170     *
     171     * @return void
     172     */
     173    public function postWasRestored(WP_Post $post): void
     174    {
     175        $action = $this->getPostTypeLabel($post->ID) . ' Restored from Trash';
     176
     177        Logtivity_Logger::log()
     178            ->setAction($action)
     179            ->setContext($post->post_title)
     180            ->setPostType($post->post_type)
     181            ->setPostId($post->ID)
     182            ->addMeta('Post Title', $post->post_title)
     183            ->send();
     184    }
     185
     186    /**
     187     * @param int $postId
     188     *
     189     * @return void
     190     */
     191    public function postPermanentlyDeleted(int $postId): void
     192    {
     193        if (
     194            $this->ignoringPostType(get_post_type($postId)) == false
     195            && $this->ignoringPostTitle(logtivity_get_the_title($postId)) == false
     196        ) {
     197            Logtivity_Logger::log()
     198                ->setAction(
     199                    $this->getPostTypeLabel($postId) . ' Permanently Deleted'
     200                )
     201                ->setContext(logtivity_get_the_title($postId))
     202                ->setPostType(get_post_type($postId))
     203                ->setPostId($postId)
     204                ->addMeta('Post Title', logtivity_get_the_title($postId))
     205                ->send();
     206        }
     207    }
     208
     209    /**
     210     * @param array  $upload
     211     * @param string $context
     212     *
     213     * @return array
     214     */
     215    public function mediaUploaded(array $upload, string $context): array
     216    {
     217        Logtivity_Logger::log()
     218            ->setAction('Attachment Uploaded')
     219            ->setContext(basename($upload['file']))
     220            ->addMeta('Url', $upload['url'])
     221            ->addMeta('Type', $upload['type'])
     222            ->addMeta('Context', $context)
     223            ->send();
     224
     225        return $upload;
     226    }
    202227}
    203228
    204 $Logtivity_Post = new Logtivity_Post;
     229new Logtivity_Post();
  • logtivity/tags/3.1.7/Logs/Logtivity_Abstract_Logger.php

    r3246625 r3264602  
    2525abstract class Logtivity_Abstract_Logger
    2626{
    27     protected $logger;
     27    /**
     28     * @var string[]
     29     */
     30    protected array $ignoredPostTypes = [
     31        'revision',
     32        'customize_changeset',
     33        'nav_menu_item',
     34        'edd_log',
     35        'edd_payment',
     36        'edd_license_log',
     37    ];
    2838
    29     protected $ignoredPostTypes = [
    30         'revision',
    31         'customize_changeset',
    32         'nav_menu_item',
    33         'edd_log',
    34         'edd_payment',
    35         'edd_license_log',
    36     ];
    37    
    38     protected $ignoredPostTitles = [
    39         'Auto Draft',
    40     ];
    41    
    42     protected $ignoredPostStatuses = ['trash'];
     39    /**
     40     * @var string[]
     41     */
     42    protected array $ignoredPostTitles = [
     43        'Auto Draft',
     44    ];
    4345
    44     public function __construct()
    45     {
    46         $this->registerHooks();
    47     }
     46    /**
     47     * @var string[]
     48     */
     49    protected array $ignoredPostStatuses = ['trash'];
    4850
    49     /**
    50      * Check against certain rules on whether we should ignore the logging of a certain post
    51      *
    52      * @param  WP_Post $post
    53      * @return bool
    54      */
    55     protected function shouldIgnore($post, $action = null)
    56     {
    57         if ($this->ignoringPostType($post->post_type)) {
    58             return true;
    59         }
     51    public function __construct()
     52    {
     53        $this->registerHooks();
     54    }
    6055
    61         if ($this->ignoringPostTitle($post->post_title)) {
    62             return true;
    63         }
     56    /**
     57     * @param WP_Post $post
     58     *
     59     * @return bool
     60     */
     61    protected function shouldIgnore(WP_Post $post): bool
     62    {
     63        return $this->ignoringPostType($post->post_type)
     64            || $this->ignoringPostTitle($post->post_title)
     65            || $this->ignoringPostStatus($post->post_status)
     66            || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE);
     67    }
    6468
    65         if ($this->ignoringPostStatus($post->post_status)) {
    66             return true;
    67         }
     69    /**
     70     * @param int $postId
     71     *
     72     * @return bool
     73     */
     74    protected function loggedRecently(int $postId): bool
     75    {
     76        $now = new DateTime();
    6877
    69         if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
    70             return true;
    71         }
     78        if ($date = get_post_meta($postId, 'logtivity_last_logged', true)) {
     79            $lastLogged = $this->sanitizeDate($date);
    7280
    73         return false;
    74     }
     81            $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp();
    7582
    76     /**
    77      * Check to see if the request is Gutenbergs second request
    78      *
    79      * @return bool
    80      */
    81     protected function loggedRecently($postId)
    82     {
    83         $date = get_post_meta($postId, 'logtivity_last_logged', true);
     83            return $diffInSeconds < 5;
     84        }
    8485
    85         if (!$date) {
    86             return false;
    87         }
     86        return false;
     87    }
    8888
    89         $now = new DateTime();
    90         $lastLogged = $this->sanitizeDate($date);
     89    /**
     90     * @param ?string $date
     91     *
     92     * @return DateTime
     93     */
     94    protected function sanitizeDate(?string $date): DateTime
     95    {
     96        try {
     97            return new DateTime($date);
    9198
    92         $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp();
     99        } catch (Throwable $e) {
     100            return new DateTime('1970-01-01');
     101        }
     102    }
    93103
    94         return $diffInSeconds < 5;
    95     }
     104    /**
     105     * Ignoring certain post statuses. Example: trash.
     106     * We already have a postWasTrashed hook so
     107     * don't need to log twice.
     108     *
     109     * @param ?string $postStatus
     110     *
     111     * @return bool
     112     */
     113    protected function ignoringPostStatus(?string $postStatus): bool
     114    {
     115        return in_array($postStatus, $this->ignoredPostStatuses);
     116    }
    96117
    97     protected function sanitizeDate( $date )
    98     {
    99         try {
    100             return new \DateTime( $date );
    101         } catch (\Throwable $e) {
    102             return new \DateTime( '1970-01-01' );
    103         } catch (\Exception $e) {
    104             return new \DateTime( '1970-01-01' );
    105         }
    106     }
     118    /**
     119     * Ignoring certain post types. Particularly system generated
     120     * that are not directly triggered by the user.
     121     *
     122     * @param ?string $postType
     123     *
     124     * @return bool
     125     */
     126    protected function ignoringPostType(?string $postType): bool
     127    {
     128        return in_array($postType, $this->ignoredPostTypes);
     129    }
    107130
    108     /**
    109      * Ignoring certain post statuses. Example: trash.
    110      * We already have a postWasTrashed hook so
    111      * don't need to log twice.
    112      *
    113      * @param  string $post_status
    114      * @return bool
    115      */
    116     protected function ignoringPostStatus($post_status)
    117     {
    118         return in_array($post_status, $this->ignoredPostStatuses);
    119     }
     131    /**
     132     * Ignore certain system generated post titles
     133     *
     134     * @param ?string $title
     135     *
     136     * @return bool
     137     */
     138    protected function ignoringPostTitle(?string $title): bool
     139    {
     140        return in_array($title, $this->ignoredPostTitles);
     141    }
    120142
    121     /**
    122      * Ignoring certain post types. Particularly system generated
    123      * that are not directly triggered by the user.
    124      *
    125      * @param  string $post_type
    126      * @return bool
    127     */
    128     protected function ignoringPostType($post_type)
    129     {
    130         return in_array($post_type, $this->ignoredPostTypes);
    131     }
     143    /**
     144     * Generate a label version of the given post ids post type
     145     *
     146     * @param int $postId
     147     *
     148     * @return string
     149    */
     150    protected function getPostTypeLabel(int $postId): string
     151    {
     152        return $this->formatLabel(get_post_type($postId));
     153    }
    132154
    133     /**
    134      * Ignore certain system generated post titles
    135      *
    136      * @param  string $title
    137      * @return bool
    138      */
    139     protected function ignoringPostTitle($title)
    140     {
    141         return in_array($title, $this->ignoredPostTitles);
    142     }
    143 
    144     /**
    145      * Generate a label version of the given post ids post type
    146      *
    147      * @param  integer $post_id
    148      * @return string
    149      */
    150     protected function getPostTypeLabel($post_id)
    151     {
    152         return $this->formatLabel(get_post_type($post_id));
    153     }
    154 
    155     protected function formatLabel($label)
    156     {
    157         return ucwords( str_replace(['_', '-'], ' ', $label) );
    158     }
     155    /**
     156     * @param string $label
     157     *
     158     * @return string
     159     */
     160    protected function formatLabel(string $label): string
     161    {
     162        return ucwords(str_replace(['_', '-'], ' ', $label));
     163    }
    159164}
  • logtivity/tags/3.1.7/Services/Logtivity_Api.php

    r3246625 r3264602  
    2525class Logtivity_Api
    2626{
    27     /**
    28     * Option class to access the plugin settings
    29     *
    30     * @var object
    31     */
    32     protected $options;
     27    /**
     28    * Option class to access the plugin settings
     29    *
     30    * @var object
     31    */
     32    protected Logtivity_Options $options;
    3333
    34     /**
    35     * Should we wait to return the response from the API?
    36     *
    37      * @var boolean
    38     */
    39     public $waitForResponse = true;
     34    /**
     35    * Should we wait to return the response from the API?
     36    *
     37     * @var bool
     38    */
     39    public bool $waitForResponse = true;
    4040
    41     /**
    42     * Definitely don't wait for a response.
    43     *
    44      * @var boolean
    45     */
    46     public $asyncOverride = false;
     41    /**
     42    * Definitely don't wait for a response.
     43    *
     44     * @var bool
     45    */
     46    public bool $asyncOverride = false;
    4747
    48     /**
    49     * The API key for either the site or team
    50     *
    51     * @var string
    52     */
    53     public $api_key;
     48    /**
     49    * The API key for either the site or team
     50    *
     51    * @var string
     52    */
     53    public string $api_key = '';
    5454
    55     public function __construct()
    56     {
    57         $this->options = new Logtivity_Options;
    58     }
     55    public function __construct()
     56    {
     57        $this->options = new Logtivity_Options();
     58    }
    5959
    60     /**
    61      * Get the API URL for the Logtivity endpoint
    62     *
    63     * @return string
    64     */
    65     public function getEndpoint($endpoint)
    66     {
    67         return logtivity_get_api_url() . $endpoint;
    68     }
     60    /**
     61     * @param string $endpoint
     62    *
     63    * @return string
     64    */
     65    public function getEndpoint(string $endpoint): string
     66    {
     67        return logtivity_get_api_url() . $endpoint;
     68    }
    6969
    70     public function post($url, $body)
    71     {
    72         return $this->makeRequest($url, $body, 'POST');
    73     }
     70    public function post(string $url, array $body)
     71    {
     72        return $this->makeRequest($url, $body);
     73    }
    7474
    75     public function get($url, $body)
    76     {
    77         return $this->makeRequest($url, $body, 'GET');
    78     }
     75    public function get($url, $body)
     76    {
     77        return $this->makeRequest($url, $body, 'GET');
     78    }
    7979
    80     public function setApiKey($api_key)
    81     {
    82         $this->api_key = $api_key;
     80    /**
     81     * @param string $apikey
     82     *
     83     * @return $this
     84     */
     85    public function setApiKey(string $apikey): self
     86    {
     87        $this->api_key = $apikey;
    8388
    84         return $this;
    85     }
     89        return $this;
     90    }
    8691
    87     public function async()
    88     {
    89         $this->asyncOverride = true;
     92    /**
     93     * @return $this
     94     */
     95    public function async(): self
     96    {
     97        $this->asyncOverride = true;
    9098
    91         return $this;
    92     }
     99        return $this;
     100    }
    93101
    94     /**
    95      * Make a request to the Logtivity API
    96      *
    97      * @param  string $url
    98      * @param  array $body
    99      * @param  string $method
     102    /**
     103     * Make a request to the Logtivity API
    100104     *
    101      * @return mixed $response
    102      */
    103     public function makeRequest(string $url, array $body, string $method = 'POST')
    104     {
    105         if (!$this->api_key) {
    106             $this->api_key = logtivity_get_api_key();
    107         }
    108         if (!$this->api_key) {
    109             return null;
    110         }
     105     * @param string $url
     106     * @param array  $body
     107     * @param string $method
     108     *
     109     * @return mixed $response
     110     */
     111    public function makeRequest(string $url, array $body, string $method = 'POST'): ?string
     112    {
     113        $this->api_key = $this->api_key ?: logtivity_get_api_key();
     114        if ($this->options->urlHash() == false) {
     115            $this->options->update(['logtivity_url_hash' => md5(home_url())], false);
     116        }
    111117
    112         if (!$this->options->urlHash()) {
    113             $this->options->update(['logtivity_url_hash' => md5(home_url())], false);
    114         }
    115         if (logtivity_has_site_url_changed()) {
    116             return null;
    117         }
     118        if ($this->api_key && logtivity_has_site_url_changed() == false) {
     119            $shouldLogLatestResponse = $this->asyncOverride == false
     120                && ($this->waitForResponse || $this->options->shouldLogLatestResponse());
    118121
    119         $shouldLogLatestResponse = !$this->asyncOverride && ($this->waitForResponse || $this->options->shouldLogLatestResponse());
     122            $response = wp_remote_post($this->getEndpoint($url), [
     123                'method'      => $method,
     124                'timeout'     => ($shouldLogLatestResponse ? 6 : 0.01),
     125                'blocking'    => $shouldLogLatestResponse,
     126                'redirection' => 5,
     127                'httpversion' => '1.0',
     128                'headers'     => [
     129                    'Authorization' => 'Bearer ' . $this->api_key,
     130                ],
     131                'body'        => $body,
     132                'cookies'     => [],
     133            ]);
    120134
    121         $response = wp_remote_post($this->getEndpoint($url), [
    122             'method' => $method,
    123             'timeout'   => ( $shouldLogLatestResponse ? 6 : 0.01),
    124             'blocking'  => ( $shouldLogLatestResponse ? true : false),
    125             'redirection' => 5,
    126             'httpversion' => '1.0',
    127             'headers' => [
    128                 'Authorization' => 'Bearer '.$this->api_key
    129             ],
    130             'body' => $body,
    131             'cookies' => array()
    132         ]);
     135            $response = wp_remote_retrieve_body($response);
    133136
    134         $response = wp_remote_retrieve_body($response);
     137            if ($response) {
     138                if ($shouldLogLatestResponse && $this->notUpdatingWidgetInCustomizer() && $method === 'POST') {
     139                    $this->options->update([
     140                        'logtivity_latest_response' => [
     141                            'date'     => date('Y-m-d H:i:s'),
     142                            'response' => print_r($response, true),
     143                        ],
     144                    ],
     145                        false
     146                    );
    135147
    136         if (empty($response)) {
    137             return $response;
    138         }
     148                    update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
    139149
    140         if ($shouldLogLatestResponse && $this->notUpdatingWidgetInCustomizer() && $method === 'POST') {
    141             $this->options->update([
    142                     'logtivity_latest_response' => [
    143                         'date' => date("Y-m-d H:i:s"),
    144                         'response' => print_r($response, true)
    145                     ]
    146                 ],
    147                 false
    148             );
     150                    $body = json_decode($response, true);
    149151
    150             update_option('logtivity_last_settings_check_in_at', ['date' => date("Y-m-d H:i:s")]);
     152                    $this->updateSettings($body);
     153                }
     154            }
     155        }
    151156
    152             $body = json_decode($response, true);
     157        return $response ?: null;
     158    }
    153159
    154             $this->updateSettings($body);
    155         }
     160    public function updateSettings($body)
     161    {
     162        if (isset($body['settings'])) {
     163            $this->options->update([
     164                'logtivity_global_disabled_logs'         => $body['settings']['disabled_logs'] ?? null,
     165                'logtivity_enable_white_label_mode'      => $body['settings']['enable_white_label_mode'] ?? null,
     166                'logtivity_disabled_error_levels'        => $body['settings']['disabled_error_levels'] ?? null,
     167                'logtivity_disable_error_logging'        => $body['settings']['disable_error_logging'] ?? null,
     168                'logtivity_hide_plugin_from_ui'          => $body['settings']['hide_plugin_from_ui'] ?? null,
     169                'logtivity_disable_default_logging'      => $body['settings']['disable_default_logging'] ?? null,
     170                'logtivity_enable_options_table_logging' => $body['settings']['enable_options_table_logging'] ?? null,
     171                'logtivity_enable_post_meta_logging'     => $body['settings']['enable_post_meta_logging'] ?? null,
     172                'logtivity_custom_plugin_name'           => $body['settings']['custom_plugin_name'] ?? null,
     173            ],
     174                false
     175            );
     176        }
     177    }
    156178
    157         return $response;
    158     }
     179    /**
     180     * You cannot call an extra update_option during a widget update so we make
     181     * sure not to log the most recent log response in this case.
     182     *
     183     * @return bool
     184     */
     185    private function notUpdatingWidgetInCustomizer(): bool
     186    {
     187        if (!isset($_POST['wp_customize'])) {
     188            return true;
     189        }
    159190
    160     public function updateSettings($body)
    161     {
    162         if (isset($body['settings'])) {
    163             $this->options->update([
    164                     'logtivity_global_disabled_logs' => $body['settings']['disabled_logs'] ?? null,
    165                     'logtivity_enable_white_label_mode' => $body['settings']['enable_white_label_mode'] ?? null,
    166                     'logtivity_disabled_error_levels' => $body['settings']['disabled_error_levels'] ?? null,
    167                     'logtivity_disable_error_logging' => $body['settings']['disable_error_logging'] ?? null,
    168                     'logtivity_hide_plugin_from_ui' => $body['settings']['hide_plugin_from_ui'] ?? null,
    169                     'logtivity_disable_default_logging' => $body['settings']['disable_default_logging'] ?? null,
    170                     'logtivity_enable_options_table_logging' => $body['settings']['enable_options_table_logging'] ?? null,
    171                     'logtivity_enable_post_meta_logging' => $body['settings']['enable_post_meta_logging'] ?? null,
    172                     'logtivity_custom_plugin_name' => $body['settings']['custom_plugin_name'] ?? null,
    173                 ],
    174                 false
    175             );
    176         }
    177     }
     191        if (!isset($_POST['action'])) {
     192            return true;
     193        }
    178194
    179     /**
    180      * You cannot call an extra update_option during a widget update so we make
    181      * sure not to log the most recent log response in this case.
    182      *
    183      * @return bool
    184      */
    185     private function notUpdatingWidgetInCustomizer()
    186     {
    187         if (!isset($_POST['wp_customize'])) {
    188             return true;
    189         }
    190 
    191         if (!isset($_POST['action'])) {
    192             return true;
    193         }
    194 
    195         return ! ($_POST['action'] === 'update-widget' && $_POST['wp_customize'] === 'on');
    196     }
     195        return !($_POST['action'] === 'update-widget' && $_POST['wp_customize'] === 'on');
     196    }
    197197}
  • logtivity/tags/3.1.7/Services/Logtivity_Logger.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528class Logtivity_Logger extends Logtivity_Api
    2629{
     
    3235     * @var bool
    3336     */
    34     public $active = true;
    35 
    36     /**
    37      * The action for the given log
    38      *
    39      * @var string
    40      */
    41     public $action;
    42 
    43     /**
    44      * The context for the given log. Could be a post title, or plugin
    45      * name, or anything to help give this log some more context.
    46      *
    47      * @var string
    48      */
    49     public $context;
    50 
    51     /**
    52      * The post type, if relevant for a given log
    53      *
    54      * @var string
    55      */
    56     public $post_type;
    57 
    58     /**
    59      * The post ID, if relevant for a given log
    60      *
    61      * @var integer
    62      */
    63     public $post_id;
    64 
    65     /**
    66      * Extra info to pass to the log
    67      *
     37    public bool $active = true;
     38
     39    /**
     40     * @var ?string
     41     */
     42    public ?string $action = null;
     43
     44    /**
     45     * @var ?string
     46     */
     47    public ?string $context = null;
     48
     49    /**
     50     * @var ?string
     51     */
     52    public ?string $post_type = null;
     53
     54    /**
     55     * @var ?int
     56     */
     57    public ?int $post_id = null;
     58
     59    /**
    6860     * @var array
    6961     */
    70     public $meta = [];
    71 
    72     /**
    73      * Extra user meta to pass to the log
    74      *
     62    public array $meta = [];
     63
     64    /**
    7565     * @var array
    7666     */
    77     public $userMeta = [];
     67    public array $userMeta = [];
    7868
    7969    /**
    8070     * When storing a log, generally we want to do this asynchronously
    81      * and so we won't wait for a response from the API by default.
    82      *
    83      * @var boolean
    84      */
    85     public $waitForResponse = false;
    86 
    87     /**
    88      * Set the user and call the parent constructor
    89      */
    90     public function __construct($user_id = null)
    91     {
    92         $this->setUser($user_id);
     71     * so we won't wait for a response from the API by default.
     72     *
     73     * @var bool
     74     */
     75    public bool $waitForResponse = false;
     76
     77    /**
     78     * @param ?int $userId
     79     */
     80    public function __construct(?int $userId = null)
     81    {
     82        $this->setUser($userId);
    9383
    9484        parent::__construct();
     
    9888     * Way into class.
    9989     *
     90     * @param ?string $action
     91     * @param ?array  $meta
     92     * @param ?int    $userId
     93     *
     94     * @return ?mixed
     95     */
     96    public static function log(?string $action = null, ?array $meta = null, ?int $userId = null)
     97    {
     98        $logtivityLogger = new Logtivity_Logger($userId);
     99
     100        if ($action === null) {
     101            return new $logtivityLogger();
     102        }
     103
     104        $logtivityLogger->setAction($action);
     105
     106        if ($meta) {
     107            $key   = $meta['key'] ?? null;
     108            $value = $meta['value'] ?? null;
     109            if ($key && $value) {
     110                $logtivityLogger->addMeta($key, $value);
     111            }
     112        }
     113
     114        return $logtivityLogger->send();
     115    }
     116
     117    /**
    100118     * @param string $action
    101      * @param string $meta
    102      * @param string $user_id
    103      * @return Logtivity_Logger::send()
    104      */
    105     public static function log($action = null, $meta = null, $user_id = null)
    106     {
    107         $Logtivity_logger = new Logtivity_Logger($user_id);
    108 
    109         if (is_null($action)) {
    110 
    111             return new $Logtivity_logger;
    112 
    113         }
    114 
    115         $Logtivity_logger->setAction($action);
    116 
    117         if ($meta) {
    118             $Logtivity_logger->addMeta($meta['key'], $meta['value']);
    119         }
    120 
    121         return $Logtivity_logger->send();
    122     }
    123 
    124     /**
    125      * Set the action string before sending
    126      *
    127      * @param string
    128      */
    129     public function setAction($action)
     119     *
     120     * @return $this
     121     */
     122    public function setAction(string $action): self
    130123    {
    131124        $this->action = $action;
     
    135128
    136129    /**
    137      * Set the context string before sending.
    138      *
    139      * @param string
    140      */
    141     public function setContext($context)
     130     * @param string $context
     131     *
     132     * @return $this
     133     */
     134    public function setContext(string $context): self
    142135    {
    143136        $this->context = $context;
     
    147140
    148141    /**
    149      * Set the post_type string before sending.
    150      *
    151      * @param string
    152      */
    153     public function setPostType($post_type)
    154     {
    155         $this->post_type = $post_type;
    156 
    157         return $this;
    158     }
    159 
    160     /**
    161      * Set the post_id before sending.
    162      *
    163      * @param integer
    164      */
    165     public function setPostId($post_id)
    166     {
    167         $this->post_id = $post_id;
    168 
    169         return $this;
    170     }
    171 
    172     /**
    173      * Add to an array any additional information you would like to pass to this log.
    174      *
     142     * @param string $postType
     143     *
     144     * @return $this
     145     */
     146    public function setPostType(string $postType): self
     147    {
     148        $this->post_type = $postType;
     149
     150        return $this;
     151    }
     152
     153    /**
     154     * @param int $postId
     155     *
     156     * @return $this
     157     */
     158    public function setPostId(int $postId): self
     159    {
     160        $this->post_id = $postId;
     161
     162        return $this;
     163    }
     164
     165    /**
    175166     * @param string $key
    176167     * @param mixed  $value
    177      * @return $this
    178      */
    179     public function addMeta($key, $value)
     168     *
     169     * @return $this
     170     */
     171    public function addMeta(string $key, $value): self
    180172    {
    181173        $this->meta[] = [
     
    190182     * Add the meta if the first condition is true
    191183     *
    192      * @param boolean $condition
    193      * @param string  $key
    194      * @param mixed   $value
    195      */
    196     public function addMetaIf($condition, $key, $value)
     184     * @param ?bool  $condition
     185     * @param string $key
     186     * @param mixed  $value
     187     *
     188     * @return $this
     189     */
     190    public function addMetaIf(?bool $condition, string $key, $value): self
    197191    {
    198192        if ($condition) {
     
    208202     * @param string $key
    209203     * @param mixed  $value
    210      * @return $this
    211      */
    212     public function addUserMeta($key, $value)
     204     *
     205     * @return $this
     206     */
     207    public function addUserMeta(string $key, $value): self
    213208    {
    214209        $this->userMeta[$key] = $value;
     
    218213
    219214    /**
    220      * Should we wait and record the response from logtivity.
    221      *
    222      * @return $this
    223      */
    224     public function waitForResponse()
     215     * @return $this
     216     */
     217    public function waitForResponse(): self
    225218    {
    226219        $this->waitForResponse = true;
     
    230223
    231224    /**
    232      * Stop this instance of Logtivity_Logger from logging
    233      *
    234      * @return $this
    235      */
    236     public function stop()
     225     * @return $this
     226     */
     227    public function stop(): self
    237228    {
    238229        $this->active = false;
     
    260251
    261252    /**
    262      * Build the data array for storing the log
    263      *
    264253     * @return array
    265254     */
    266     protected function getData()
     255    protected function getData(): array
    267256    {
    268257        return [
     
    284273     * @return array
    285274     */
    286     public function getUserMeta()
     275    public function getUserMeta(): array
    287276    {
    288277        return (array)apply_filters('wp_logtivity_get_user_meta', $this->userMeta);
     
    294283     * @return array
    295284     */
    296     public function getMeta()
     285    public function getMeta(): array
    297286    {
    298287        return (array)apply_filters('wp_logtivity_get_meta', $this->meta);
     
    300289
    301290    /**
    302      * Maybe get the users profile link
    303      *
    304      * @return string|false
    305      */
    306     protected function maybeAddProfileLink()
    307     {
    308         if (!$this->options->shouldStoreProfileLink()) {
    309             return;
    310         }
    311 
    312         if (!$this->user->isLoggedIn()) {
    313             return;
    314         }
    315 
    316         $profileLink = $this->user->profileLink();
    317 
    318         if ($profileLink == '') {
    319             return null;
    320         }
    321 
    322         return $this->addUserMeta('Profile Link', $profileLink);
     291     * @return $this
     292     */
     293    protected function maybeAddProfileLink(): self
     294    {
     295        if (
     296            $this->options->shouldStoreProfileLink()
     297            && $this->user->isLoggedIn()
     298            && ($profileLink = $this->user->profileLink())
     299        ) {
     300            $this->addUserMeta('Profile Link', $profileLink);
     301        }
     302
     303        return $this;
    323304    }
    324305}
  • logtivity/tags/3.1.7/Services/Logtivity_User_Logger_Trait.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528trait Logtivity_User_Logger_Trait
    2629{
    27     /**
    28      * Logtivity_Wp_User
    29      *
    30      * @var object
    31      */
    32     public $user;
    33    
    34     /**
    35      * Set the user for the current log instance
    36      *
    37      * @param integer $user_id
    38      */
    39     public function setUser($user_id = null)
    40     {
    41         $this->user = new Logtivity_Wp_User($user_id);
     30    /**
     31     * @var Logtivity_Wp_User
     32     */
     33    public Logtivity_Wp_User $user;
    4234
    43         return $this;
    44     }
     35    /**
     36     * @param ?int $userId
     37     *
     38     * @return $this
     39     */
     40    public function setUser(?int $userId = null): self
     41    {
     42        $this->user = new Logtivity_Wp_User($userId);
    4543
    46     /**
    47      * Protected function to get the User ID if the user is logged in
    48      *
    49      * @return mixed string|integer
    50      */
    51     protected function getUserID()
    52     {
    53         if (!$this->options->shouldStoreUserId()) {
    54             return;
    55         }
     44        return $this;
     45    }
    5646
    57         if (!$this->user->isLoggedIn()) {
    58             return;
    59         }
     47    /**
     48     * @return ?int
     49     */
     50    protected function getUserID(): ?int
     51    {
     52        if (
     53            $this->options->shouldStoreUserId()
     54            && $this->user->isLoggedIn()
     55        ) {
     56            return $this->user->id();
     57        }
    6058
    61         return $this->user->id();
    62     }
     59        return null;
     60    }
    6361
    64     /**
    65      * Maybe get the users IP address
    66      *
    67      * @return string|false
    68      */
    69     protected function maybeGetUsersIp()
    70     {
    71         if (!$this->options->shouldStoreIp()) {
    72             return;
    73         }
     62    /**
     63     * @return ?string
     64     */
     65    protected function maybeGetUsersIp(): ?string
     66    {
     67        if ($this->options->shouldStoreIp()) {
     68            $ipAddress = filter_var(
     69                ($_SERVER['HTTP_CLIENT_IP'] ?? '')
     70                    ?: ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? '')
     71                    ?: $_SERVER['REMOTE_ADDR'] ?? '',
     72                FILTER_VALIDATE_IP
     73            );
    7474
    75         if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
    76             //check ip from share internet
    77             return $_SERVER['HTTP_CLIENT_IP'];
    78         } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
    79             //to check ip is pass from proxy
    80             return $_SERVER['HTTP_X_FORWARDED_FOR'];
    81         } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
    82             return $_SERVER['REMOTE_ADDR'];
    83         }
    84     }
     75            return $ipAddress ?: null;
     76        }
    8577
    86     /**
    87      * Maybe get the users username
    88      *
    89      * @return string|false
    90      */
    91     protected function maybeGetUsersUsername()
    92     {
    93         if (!$this->options->shouldStoreUsername()) {
    94             return null;
    95         }
     78        return null;
     79    }
    9680
    97         if (!$this->user->isLoggedIn()) {
    98             return;
    99         }
     81    /**
     82     * @return ?string
     83     */
     84    protected function maybeGetUsersUsername(): ?string
     85    {
     86        if (
     87            $this->options->shouldStoreUsername()
     88            && $this->user->isLoggedIn()
     89        ) {
     90            return $this->user->userLogin();
     91        }
    10092
    101         return $this->user->userLogin();
    102     }
     93        return null;
     94    }
    10395}
  • logtivity/tags/3.1.7/logtivity.php

    r3251888 r3264602  
    55 * Plugin URI:  https://logtivity.io
    66 * Description: Record activity logs and errors logs across all your WordPress sites.
    7  * Version:     3.1.6
     7 * Version:     3.1.7
    88 * Author:      Logtivity
    99 * Text Domain: logtivity
     
    4343     * @var string
    4444     */
    45     protected string $version = '3.1.6';
     45    protected string $version = '3.1.7';
    4646
    4747    /**
     
    129129        $this->loadDependencies();
    130130
    131         register_activation_hook(__FILE__, [$this, 'activated']);
     131        add_action('plugins_loaded', [$this, 'updateCheck']);
    132132
    133133        add_action('upgrader_process_complete', [$this, 'upgradeProcessComplete'], 10, 2);
     
    138138
    139139        add_filter('plugin_action_links_' . plugin_basename(__FILE__), [$this, 'addSettingsLinkFromPluginsPage']);
    140     }
    141 
    142     public function loadDependencies()
     140
     141        register_activation_hook(__FILE__, [$this, 'activated']);
     142    }
     143
     144    /**
     145     * @return void
     146     */
     147    public function loadDependencies(): void
    143148    {
    144149        foreach ($this->dependencies as $filePath) {
     
    157162    }
    158163
    159     public function loadFile($filePath)
     164    /**
     165     * @param string $filePath
     166     *
     167     * @return void
     168     */
     169    public function loadFile(string $filePath): void
    160170    {
    161171        require_once plugin_dir_path(__FILE__) . $filePath . '.php';
     
    172182    }
    173183
    174     public function maybeLoadLogClasses()
     184    /**
     185     * @return void
     186     */
     187    public function maybeLoadLogClasses(): void
    175188    {
    176189        foreach ($this->logClasses as $filePath) {
     
    179192    }
    180193
    181     public function loadIntegrationDependencies()
     194    /**
     195     * @return void
     196     */
     197    public function loadIntegrationDependencies(): void
    182198    {
    183199        foreach ($this->integrationDependencies as $key => $value) {
     
    190206    }
    191207
    192     public static function log($action = null, $meta = null, $user_id = null)
    193     {
    194         return Logtivity_Logger::log($action, $meta, $user_id);
     208    /**
     209     * @param ?string $action
     210     * @param ?array  $meta
     211     * @param ?int    $userId
     212     *
     213     * @return ?mixed
     214     */
     215    public static function log(?string $action = null, ?array $meta = null, ?int $userId = null)
     216    {
     217        return Logtivity_Logger::log($action, $meta, $userId);
    195218    }
    196219
     
    206229
    207230    /**
    208      * @param $upgraderObject
    209      * @param $options
    210      *
    211      * @return null|void
    212      */
    213     public function upgradeProcessComplete($upgraderObject, $options)
     231     * Review updates based on version
     232     *
     233     * @return void
     234     */
     235    public function updateCheck(): void
     236    {
     237        $currentVersion = get_option('logtivity_version');
     238
     239        if (version_compare($currentVersion, '3.1.7', '<')) {
     240            static::checkCapabilities();
     241        }
     242
     243        update_option('logtivity_version', $this->version);
     244    }
     245
     246    /**
     247     * @param WP_Upgrader $upgraderObject
     248     * @param array       $options
     249     *
     250     * @return void
     251     */
     252    public function upgradeProcessComplete(WP_Upgrader $upgraderObject, array $options): void
    214253    {
    215254        $type   = $options['type'] ?? null;
     
    271310
    272311    /**
     312     * Custom capabilities added prior to v3.1.7
     313     *
    273314     * @return void
    274315     */
    275316    public static function checkCapabilities(): void
    276317    {
    277         /** @var WP_User[] $users */
    278         $users = get_users();
    279         $caps  = [];
    280         foreach ($users as $user) {
    281             foreach ($user->roles as $role) {
    282                 $r    = get_role($role);
    283                 $caps = array_merge($caps, $r->capabilities ?: []);
    284             }
    285         }
    286 
    287         if (isset($caps[static::ACCESS_LOGS]) == false || isset($caps[static::ACCESS_SETTINGS]) == false) {
     318        $capabilities = array_filter(
     319            array_keys(logtivity_get_capabilities()),
     320            function (string $capability): bool {
     321                return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
     322            }
     323        );
     324
     325        if ($capabilities == false) {
    288326            // Make sure at least admins can access us
    289327            if ($role = get_role('administrator')) {
  • logtivity/tags/3.1.7/readme.txt

    r3251888 r3264602  
    55Requires at least: 4.7
    66Tested up to: 6.7.2
    7 Stable tag: 3.1.6
     7Stable tag: 3.1.7
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    263263== Changelog ==
    264264
    265 = 3.1.6 =
    266 
    267 * Update: Automatically check and ensure capabilities exist
     265= 3.1.7 =
     266
     267* Check for installed custom capabilities
     268* Improved input sanitization
     269
     270_Release Date - Thursday, March 27 2025_
    268271
    269272= 3.1.5 =
     
    271274* Add: new capabilities - 'view logs', 'view log settings'
    272275
    273 _Release Date - Pending_
     276_Release Date - Tuesday, February 25th 2025_
    274277
    275278= 3.1.4 =
  • logtivity/trunk/Admin/Logtivity_Admin.php

    r3251888 r3264602  
    102102    public function registerOptionsPage()
    103103    {
    104         Logtivity::checkCapabilities();
    105 
    106104        if (!apply_filters('logtivity_hide_from_menu', false)) {
    107105            add_menu_page(
  • logtivity/trunk/Errors/Logtivity_Error_Logger.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528class Logtivity_Error_Logger extends Logtivity_Api
    2629{
    27     use Logtivity_User_Logger_Trait;
     30    use Logtivity_User_Logger_Trait;
    2831
    29     protected $active = true;
     32    /**
     33     * @var bool
     34     */
     35    protected bool $active = true;
    3036
    31     protected $error;
     37    /**
     38     * @var array
     39     */
     40    protected array $error = [];
    3241
    33     protected static $recordedErrors = [];
     42    /**
     43     * @var string[]
     44     */
     45    protected static array $recordedErrors = [];
    3446
    35     public function __construct($error)
    36     {
    37         $this->error = $error;
     47    /**
     48     * @param array $error
     49     */
     50    public function __construct(array $error)
     51    {
     52        $this->error = $error;
    3853
    39         $this->setUser();
     54        $this->setUser();
    4055
    41         parent::__construct();
    42     }
     56        parent::__construct();
     57    }
    4358
    44     public function stop()
    45     {
    46         $this->active = false;
     59    /**
     60     * @return $this
     61     */
     62    public function stop(): self
     63    {
     64        $this->active = false;
    4765
    48         return $this;
    49     }
     66        return $this;
     67    }
    5068
    51     public function send()
    52     {
    53         if (in_array($this->error['message'], self::$recordedErrors)) {
    54             return;
    55         }
     69    /**
     70     * @return ?string
     71     */
     72    public function send(): ?string
     73    {
     74        $message = $this->error['message'] ?? '';
     75        if (in_array($message, self::$recordedErrors) === false) {
     76            self::$recordedErrors[] = $this->error['message'];
    5677
    57         self::$recordedErrors[] = $this->error['message'];
     78            do_action('wp_logtivity_error_logger_instance', $this);
    5879
    59         do_action('wp_logtivity_error_logger_instance', $this);
     80            if ($this->active) {
     81                $response = $this->async()->makeRequest('/errors/store', $this->getData());
     82            }
     83        }
    6084
    61         if (!$this->active) {
    62             return;
    63         }
     85        return $response ?? null;
     86    }
    6487
    65         return $this->async()
    66                     ->makeRequest(
    67                         '/errors/store',
    68                         $this->getData()
    69                     );
    70     }
     88    /**
     89     * @return array
     90     */
     91    public function getData(): array
     92    {
     93        $error = explode('Stack trace:', $this->error['message']);
    7194
    72     public function getData()
    73     {
    74         $error = explode('Stack trace:', $this->error['message']);
     95        return [
     96            'type'               => $this->getErrorLevel($this->error['type']) ?? null,
     97            'message'            => $error[0],
     98            'stack_trace'        => $this->generateStackTrace(
     99                [
     100                    'file' => $this->error['file'] ?? null,
     101                    'line' => $this->error['line'] ?? null,
     102                ],
     103                $error[1] ?? null
     104            ),
     105            'file'               => $this->error['file'] ?? null,
     106            'line'               => $this->error['line'] ?? null,
     107            'user_id'            => $this->getUserID(),
     108            'username'           => $this->maybeGetUsersUsername(),
     109            'ip_address'         => $this->maybeGetUsersIp(),
     110            'user_authenticated' => $this->user->isLoggedIn(),
     111            'url'                => $this->getCurrentUrl(),
     112            'method'             => $this->getRequestMethod(),
     113            'php_version'        => phpversion(),
     114            'level'              => $this->error['level'] ?? null,
     115        ];
     116    }
    75117
    76         return [
    77             'type' => $this->getErrorLevel($this->error['type']) ?? null,
    78             'message' => $error[0],
    79             'stack_trace' => $this->generateStackTrace(
    80                 [
    81                     'file' => $this->error['file'] ?? null,
    82                     'line' => $this->error['line'] ?? null,
    83                 ],
    84                 $error[1] ?? null
    85             ),
    86             'file' => $this->error['file'] ?? null,
    87             'line' => $this->error['line'] ?? null,
    88             'user_id' => $this->getUserID(),
    89             'username' => $this->maybeGetUsersUsername(),
    90             'ip_address' => $this->maybeGetUsersIp(),
    91             'user_authenticated' => $this->user->isLoggedIn(),
    92             'url' => $this->getCurrentUrl(),
    93             'method' => $this->getRequestMethod(),
    94             'php_version' => phpversion(),
    95             'level' => $this->error['level'] ?? null,
    96         ];
    97     }
     118    /**
     119     * @param array   $line
     120     * @param ?string $stackTrace
     121     *
     122     * @return array
     123     */
     124    private function generateStackTrace(array $line, ?string $stackTrace): array
     125    {
     126        $stackTraceObject = new Logtivity_Stack_Trace();
    98127
    99     private function generateStackTrace($line, $stackTrace)
    100     {
    101         $stackTraceObject = new Logtivity_Stack_Trace();
     128        if (isset($this->error['stack_trace'])) {
     129            return $stackTraceObject->createFromArray($this->error['stack_trace']);
     130        }
    102131
    103         if (isset($this->error['stack_trace'])) {
    104             return $stackTraceObject->createFromArray($this->error['stack_trace']);
    105         }
     132        return array_merge(
     133            [$stackTraceObject->createFileObject($line['file'], $line['line'])],
     134            $stackTraceObject->createFromString($stackTrace)
     135        );
     136    }
    106137
    107         return array_merge(
    108             [$stackTraceObject->createFileObject($line['file'], $line['line'])],
    109             $stackTraceObject->createFromString($stackTrace)
    110         );
    111     }
     138    /**
     139     * @return string
     140     */
     141    private function getRequestMethod(): string
     142    {
     143        return sanitize_text_field($_SERVER['REQUEST_METHOD'] ?? '');
     144    }
    112145
    113     private function getRequestMethod()
    114     {
    115         return $_SERVER['REQUEST_METHOD'] ?? null;
    116     }
     146    /**
     147     * @return ?string
     148     */
     149    private function getCurrentUrl(): ?string
     150    {
     151        $host = sanitize_text_field($_SERVER['HTTP_HOST'] ?? null);
     152        if ($host) {
     153            $ssl = sanitize_text_field($_SERVER['HTTPS'] ?? 'off') != 'off'
     154                || sanitize_text_field($_SERVER['SERVER_PORT'] ?? '80') == 443;
    117155
    118     private function getCurrentUrl()
    119     {
    120         if (!isset($_SERVER['HTTP_HOST'])) {
    121             return;
    122         }
     156            $protocol = $ssl ? 'https://' : 'http://';
    123157
    124         $protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
     158            $path = sanitize_text_field($_SERVER['REQUEST_URI'] ?? '');
    125159
    126         $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    127        
    128         if ($url == $protocol) {
    129             return;
    130         }
     160            $url = sanitize_url($protocol . $host . $path);
    131161
    132         return $url;
    133     }
     162            if ($url != $protocol) {
     163                return $url;
     164            }
     165        }
    134166
    135     private function getErrorLevel($level)
    136     {
    137         $errorlevels = logtivity_get_error_levels();
     167        return null;
     168    }
    138169
    139         return isset($errorlevels[$level]) ? $errorlevels[$level] : $level;
    140     }
     170    /**
     171     * @param $level
     172     *
     173     * @return string
     174     */
     175    private function getErrorLevel($level): string
     176    {
     177        $errorLevels = logtivity_get_error_levels();
     178
     179        return $errorLevels[$level] ?? $level;
     180    }
    141181}
  • logtivity/trunk/Helpers/Helpers.php

    r3246625 r3264602  
    120120    $hash = (new Logtivity_Options())->urlHash();
    121121
    122     if (!$hash) {
    123         return false;
    124     }
    125 
    126     return $hash !== md5(home_url());
     122    return $hash != md5(home_url());
    127123}
    128124
     
    145141    return $errorLevels;
    146142}
     143
     144/**
     145 * Get all known capabilities
     146 *
     147 * @return array
     148 */
     149function logtivity_get_capabilities(): array
     150{
     151    global $wp_roles;
     152
     153    $capabilities = [];
     154    foreach ($wp_roles->roles as $key => $role ) {
     155        $capabilities = array_merge($capabilities, $role['capabilities']);
     156    }
     157
     158    return $capabilities;
     159}
  • logtivity/trunk/Logs/Core/Logtivity_Post.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
     26// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     27// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     28
    2529class Logtivity_Post extends Logtivity_Abstract_Logger
    2630{
    27     protected $disableUpdateLog = false;
    28    
    29     protected $action;
    30 
    31     public function registerHooks()
    32     {
    33         add_action( 'transition_post_status', [$this, 'postStatusChanged'], 10, 3);
    34         add_action( 'save_post', array( $this, 'postWasUpdated' ), 10, 3 );
    35         add_action( 'wp_trash_post', [$this, 'postWasTrashed'], 10, 1 );
    36         add_filter('wp_handle_upload', [$this, 'mediaUploaded'], 10, 2);
    37         add_action('delete_post', [$this, 'postPermanentlyDeleted'], 10, 1);
    38         add_filter( 'wp_ajax_save-attachment', [$this, 'mediaMetaUpdated'], -1 );
    39     }
    40 
    41     public function postStatusChanged($new_status, $old_status, $post)
    42     {
    43         if ($this->shouldIgnore($post)) {
    44             return;
    45         }
    46 
    47         if ($old_status == 'trash') {
    48             $this->disableUpdateLog = true;
    49             return $this->postWasRestored($post);
    50         }
    51 
    52         if ($old_status != 'publish' && $new_status == 'publish') {
    53             $this->action = $this->getPostTypeLabel($post->ID) . ' Published';
    54             return;
    55         }
    56 
    57         if ($old_status == 'publish' && $new_status == 'draft') {
    58             $this->action = $this->getPostTypeLabel($post->ID) . ' Unpublished';
    59             return;
    60         }
    61 
    62         if ($old_status != $new_status) {
    63             Logtivity_Logger::log()
    64                 ->setAction(
    65                     $this->getPostTypeLabel($post->ID) . ' Status changed from '.$old_status.' to '.$new_status
    66                 )
    67                 ->setContext($post->post_title)
    68                 ->setPostType($post->post_type)
    69                 ->setPostId($post->ID)
    70                 ->send();
    71         }
    72 
    73     }
    74 
    75     /**
    76      * Post was updated or created. ignoring certain auto save system actions
    77      *
    78      * @param  integer $post_id
    79      * @param  WP_Post $post   
    80      * @param  bool $update
    81      * @return void
    82      */
    83     public function postWasUpdated($post_id, $post, $update)
    84     {
    85         if ($this->disableUpdateLog) {
    86             return;
    87         }
    88 
    89         if ($this->shouldIgnore($post)) {
    90             return;
    91         }
    92 
    93         if ($this->loggedRecently($post->ID)) {
    94             return true;
    95         }
    96 
    97         $revision = $this->getRevision($post_id);
    98 
    99         Logtivity_Logger::log()
    100             ->setAction($this->action ?? $this->getPostTypeLabel($post->ID) . ' Updated')
    101             ->setContext($post->post_title)
    102             ->setPostType($post->post_type)
    103             ->setPostId($post->ID)
    104             ->addMeta('Post Title', $post->post_title)
    105             ->addMeta('Post Status', $post->post_status)
    106             ->addMetaIf($revision, 'View Revision', $revision)
    107             ->send();
    108 
    109         update_post_meta($post_id, 'logtivity_last_logged', (new \DateTime())->format('Y-m-d H:i:s'));
    110     }
    111 
    112     private function getRevision( $post_id )
    113     {
    114         $revisions = wp_get_post_revisions( $post_id );
    115         if ( ! empty( $revisions ) ) {
    116             $revision = array_shift( $revisions );
    117             return $this->getRevisionLink( $revision->ID );
    118         }
    119     }
    120 
    121     private function getRevisionLink( $revision_id )
    122     {
    123         return ! empty( $revision_id ) ? add_query_arg( 'revision', $revision_id, admin_url( 'revision.php' ) ) : null;
    124     }
    125 
    126     public function postWasTrashed($post_id)
    127     {
    128         if (get_post_type($post_id) == 'customize_changeset') {
    129             return;
    130         }
    131        
    132         return Logtivity_Logger::log()
    133             ->setAction($this->getPostTypeLabel($post_id) . ' Trashed')
    134             ->setContext(logtivity_get_the_title($post_id))
    135             ->setPostType(get_post_type($post_id))
    136             ->setPostId($post_id)
    137             ->addMeta('Post Title', logtivity_get_the_title($post_id))
    138             ->send();
    139     }
    140 
    141     public function postWasRestored($post)
    142     {
    143         return Logtivity_Logger::log()
    144             ->setAction(
    145                 $this->getPostTypeLabel($post->ID) . ' Restored from Trash'
    146             )
    147             ->setContext($post->post_title)
    148             ->setPostType($post->post_type)
    149             ->setPostId($post->ID)
    150             ->addMeta('Post Title', $post->post_title)
    151             ->send();
    152     }
    153 
    154     public function postPermanentlyDeleted($post_id)
    155     {
    156         if ($this->ignoringPostType(get_post_type($post_id))) {
    157             return;
    158         }
    159 
    160         if ($this->ignoringPostTitle(logtivity_get_the_title($post_id))) {
    161             return;
    162         }
    163 
    164         return Logtivity_Logger::log()
    165             ->setAction(
    166                 $this->getPostTypeLabel($post_id) . ' Permanently Deleted'
    167             )
    168             ->setContext(logtivity_get_the_title($post_id))
    169             ->setPostType(get_post_type($post_id))
    170             ->setPostId($post_id)
    171             ->addMeta('Post Title', logtivity_get_the_title($post_id))
    172             ->send();
    173     }
    174 
    175     public function mediaUploaded($upload, $context)
    176     {
    177         Logtivity_Logger::log()
    178             ->setAction('Attachment Uploaded')
    179             ->setContext(basename($upload['file']))
    180             ->addMeta('Url', $upload['url'])
    181             ->addMeta('Type', $upload['type'])
    182             ->addMeta('Context', $context)
    183             ->send();
    184 
    185         return $upload;
    186     }
    187 
    188     public function mediaMetaUpdated()
    189     {
    190         $post_id = absint($_POST['id']);
    191 
    192         if ($post_id) {
    193             Logtivity_Logger::log()
    194                 ->setAction('Attachment Meta Updated.')
    195                 ->addMeta("Media ID", $post_id)
    196                 ->addMeta("Changes", ( isset($_POST['changes']) ? $_POST['changes'] : null))
    197                 ->send();
    198         }
    199 
    200         return $post;
    201     }
     31    /**
     32     * @var bool
     33     */
     34    protected bool $disableUpdateLog = false;
     35
     36    /**
     37     * @var ?string
     38     */
     39    protected ?string $action = null;
     40
     41    /**
     42     * @return void
     43     */
     44    public function registerHooks(): void
     45    {
     46        add_action('transition_post_status', [$this, 'postStatusChanged'], 10, 3);
     47        add_action('save_post', [$this, 'postWasUpdated'], 10, 3);
     48        add_action('wp_trash_post', [$this, 'postWasTrashed']);
     49        add_action('delete_post', [$this, 'postPermanentlyDeleted']);
     50
     51        add_filter('wp_handle_upload', [$this, 'mediaUploaded'], 10, 2);
     52    }
     53
     54    /**
     55     * @param string  $newStatus
     56     * @param string  $oldStatus
     57     * @param WP_Post $post
     58     *
     59     * @return void
     60     */
     61    public function postStatusChanged(string $newStatus, string $oldStatus, WP_Post $post): void
     62    {
     63        if ($this->shouldIgnore($post) == false) {
     64            if ($oldStatus == 'trash') {
     65                $this->disableUpdateLog = true;
     66                $this->postWasRestored($post);
     67
     68            } elseif ($oldStatus != 'publish' && $newStatus == 'publish') {
     69                $this->action = $this->getPostTypeLabel($post->ID) . ' Published';
     70
     71            } elseif ($oldStatus == 'publish' && $newStatus == 'draft') {
     72                $this->action = $this->getPostTypeLabel($post->ID) . ' Unpublished';
     73
     74            } elseif ($oldStatus != $newStatus) {
     75                $action = sprintf(
     76                    '%s Status changed from %s to %s',
     77                    $this->getPostTypeLabel($post->ID),
     78                    $oldStatus,
     79                    $newStatus
     80                );
     81
     82                Logtivity_Logger::log()
     83                    ->setAction($action)
     84                    ->setContext($post->post_title)
     85                    ->setPostType($post->post_type)
     86                    ->setPostId($post->ID)
     87                    ->send();
     88            }
     89        }
     90    }
     91
     92    /**
     93     * Post was updated or created. ignoring certain auto save system actions
     94     *
     95     * @param int     $postId
     96     * @param WP_Post $post
     97     *
     98     * @return void
     99     */
     100    public function postWasUpdated(int $postId, WP_Post $post): void
     101    {
     102        if (
     103            $this->disableUpdateLog == false
     104            && $this->shouldIgnore($post) == false
     105            && $this->loggedRecently($post->ID) == false
     106        ) {
     107            $revision = $this->getRevision($postId);
     108
     109            Logtivity_Logger::log()
     110                ->setAction($this->action ?: $this->getPostTypeLabel($post->ID) . ' Updated')
     111                ->setContext($post->post_title)
     112                ->setPostType($post->post_type)
     113                ->setPostId($post->ID)
     114                ->addMeta('Post Title', $post->post_title)
     115                ->addMeta('Post Status', $post->post_status)
     116                ->addMetaIf($revision, 'View Revision', $revision)
     117                ->send();
     118
     119            update_post_meta($postId, 'logtivity_last_logged', (new DateTime())->format('Y-m-d H:i:s'));
     120        }
     121    }
     122
     123    /**
     124     * @param int $postId
     125     *
     126     * @return ?string
     127     */
     128    protected function getRevision(int $postId): ?string
     129    {
     130        if ($revisions = wp_get_post_revisions($postId)) {
     131            $revision = array_shift($revisions);
     132
     133            $revision = $this->getRevisionLink($revision->ID);
     134        }
     135
     136        return $revision ?? null;
     137    }
     138
     139    /**
     140     * @param null|int $revisionId
     141     * @return null|string
     142     */
     143    private function getRevisionLink(?int $revisionId): ?string
     144    {
     145        return $revisionId
     146            ? add_query_arg('revision', $revisionId, admin_url('revision.php'))
     147            : null;
     148    }
     149
     150    /**
     151     * @param int $postId
     152     *
     153     * @return void
     154     */
     155    public function postWasTrashed(int $postId): void
     156    {
     157        if (get_post_type($postId) != 'customize_changeset') {
     158            Logtivity_Logger::log()
     159                ->setAction($this->getPostTypeLabel($postId) . ' Trashed')
     160                ->setContext(logtivity_get_the_title($postId))
     161                ->setPostType(get_post_type($postId))
     162                ->setPostId($postId)
     163                ->addMeta('Post Title', logtivity_get_the_title($postId))
     164                ->send();
     165        }
     166    }
     167
     168    /**
     169     * @param WP_Post $post
     170     *
     171     * @return void
     172     */
     173    public function postWasRestored(WP_Post $post): void
     174    {
     175        $action = $this->getPostTypeLabel($post->ID) . ' Restored from Trash';
     176
     177        Logtivity_Logger::log()
     178            ->setAction($action)
     179            ->setContext($post->post_title)
     180            ->setPostType($post->post_type)
     181            ->setPostId($post->ID)
     182            ->addMeta('Post Title', $post->post_title)
     183            ->send();
     184    }
     185
     186    /**
     187     * @param int $postId
     188     *
     189     * @return void
     190     */
     191    public function postPermanentlyDeleted(int $postId): void
     192    {
     193        if (
     194            $this->ignoringPostType(get_post_type($postId)) == false
     195            && $this->ignoringPostTitle(logtivity_get_the_title($postId)) == false
     196        ) {
     197            Logtivity_Logger::log()
     198                ->setAction(
     199                    $this->getPostTypeLabel($postId) . ' Permanently Deleted'
     200                )
     201                ->setContext(logtivity_get_the_title($postId))
     202                ->setPostType(get_post_type($postId))
     203                ->setPostId($postId)
     204                ->addMeta('Post Title', logtivity_get_the_title($postId))
     205                ->send();
     206        }
     207    }
     208
     209    /**
     210     * @param array  $upload
     211     * @param string $context
     212     *
     213     * @return array
     214     */
     215    public function mediaUploaded(array $upload, string $context): array
     216    {
     217        Logtivity_Logger::log()
     218            ->setAction('Attachment Uploaded')
     219            ->setContext(basename($upload['file']))
     220            ->addMeta('Url', $upload['url'])
     221            ->addMeta('Type', $upload['type'])
     222            ->addMeta('Context', $context)
     223            ->send();
     224
     225        return $upload;
     226    }
    202227}
    203228
    204 $Logtivity_Post = new Logtivity_Post;
     229new Logtivity_Post();
  • logtivity/trunk/Logs/Logtivity_Abstract_Logger.php

    r3246625 r3264602  
    2525abstract class Logtivity_Abstract_Logger
    2626{
    27     protected $logger;
     27    /**
     28     * @var string[]
     29     */
     30    protected array $ignoredPostTypes = [
     31        'revision',
     32        'customize_changeset',
     33        'nav_menu_item',
     34        'edd_log',
     35        'edd_payment',
     36        'edd_license_log',
     37    ];
    2838
    29     protected $ignoredPostTypes = [
    30         'revision',
    31         'customize_changeset',
    32         'nav_menu_item',
    33         'edd_log',
    34         'edd_payment',
    35         'edd_license_log',
    36     ];
    37    
    38     protected $ignoredPostTitles = [
    39         'Auto Draft',
    40     ];
    41    
    42     protected $ignoredPostStatuses = ['trash'];
     39    /**
     40     * @var string[]
     41     */
     42    protected array $ignoredPostTitles = [
     43        'Auto Draft',
     44    ];
    4345
    44     public function __construct()
    45     {
    46         $this->registerHooks();
    47     }
     46    /**
     47     * @var string[]
     48     */
     49    protected array $ignoredPostStatuses = ['trash'];
    4850
    49     /**
    50      * Check against certain rules on whether we should ignore the logging of a certain post
    51      *
    52      * @param  WP_Post $post
    53      * @return bool
    54      */
    55     protected function shouldIgnore($post, $action = null)
    56     {
    57         if ($this->ignoringPostType($post->post_type)) {
    58             return true;
    59         }
     51    public function __construct()
     52    {
     53        $this->registerHooks();
     54    }
    6055
    61         if ($this->ignoringPostTitle($post->post_title)) {
    62             return true;
    63         }
     56    /**
     57     * @param WP_Post $post
     58     *
     59     * @return bool
     60     */
     61    protected function shouldIgnore(WP_Post $post): bool
     62    {
     63        return $this->ignoringPostType($post->post_type)
     64            || $this->ignoringPostTitle($post->post_title)
     65            || $this->ignoringPostStatus($post->post_status)
     66            || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE);
     67    }
    6468
    65         if ($this->ignoringPostStatus($post->post_status)) {
    66             return true;
    67         }
     69    /**
     70     * @param int $postId
     71     *
     72     * @return bool
     73     */
     74    protected function loggedRecently(int $postId): bool
     75    {
     76        $now = new DateTime();
    6877
    69         if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
    70             return true;
    71         }
     78        if ($date = get_post_meta($postId, 'logtivity_last_logged', true)) {
     79            $lastLogged = $this->sanitizeDate($date);
    7280
    73         return false;
    74     }
     81            $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp();
    7582
    76     /**
    77      * Check to see if the request is Gutenbergs second request
    78      *
    79      * @return bool
    80      */
    81     protected function loggedRecently($postId)
    82     {
    83         $date = get_post_meta($postId, 'logtivity_last_logged', true);
     83            return $diffInSeconds < 5;
     84        }
    8485
    85         if (!$date) {
    86             return false;
    87         }
     86        return false;
     87    }
    8888
    89         $now = new DateTime();
    90         $lastLogged = $this->sanitizeDate($date);
     89    /**
     90     * @param ?string $date
     91     *
     92     * @return DateTime
     93     */
     94    protected function sanitizeDate(?string $date): DateTime
     95    {
     96        try {
     97            return new DateTime($date);
    9198
    92         $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp();
     99        } catch (Throwable $e) {
     100            return new DateTime('1970-01-01');
     101        }
     102    }
    93103
    94         return $diffInSeconds < 5;
    95     }
     104    /**
     105     * Ignoring certain post statuses. Example: trash.
     106     * We already have a postWasTrashed hook so
     107     * don't need to log twice.
     108     *
     109     * @param ?string $postStatus
     110     *
     111     * @return bool
     112     */
     113    protected function ignoringPostStatus(?string $postStatus): bool
     114    {
     115        return in_array($postStatus, $this->ignoredPostStatuses);
     116    }
    96117
    97     protected function sanitizeDate( $date )
    98     {
    99         try {
    100             return new \DateTime( $date );
    101         } catch (\Throwable $e) {
    102             return new \DateTime( '1970-01-01' );
    103         } catch (\Exception $e) {
    104             return new \DateTime( '1970-01-01' );
    105         }
    106     }
     118    /**
     119     * Ignoring certain post types. Particularly system generated
     120     * that are not directly triggered by the user.
     121     *
     122     * @param ?string $postType
     123     *
     124     * @return bool
     125     */
     126    protected function ignoringPostType(?string $postType): bool
     127    {
     128        return in_array($postType, $this->ignoredPostTypes);
     129    }
    107130
    108     /**
    109      * Ignoring certain post statuses. Example: trash.
    110      * We already have a postWasTrashed hook so
    111      * don't need to log twice.
    112      *
    113      * @param  string $post_status
    114      * @return bool
    115      */
    116     protected function ignoringPostStatus($post_status)
    117     {
    118         return in_array($post_status, $this->ignoredPostStatuses);
    119     }
     131    /**
     132     * Ignore certain system generated post titles
     133     *
     134     * @param ?string $title
     135     *
     136     * @return bool
     137     */
     138    protected function ignoringPostTitle(?string $title): bool
     139    {
     140        return in_array($title, $this->ignoredPostTitles);
     141    }
    120142
    121     /**
    122      * Ignoring certain post types. Particularly system generated
    123      * that are not directly triggered by the user.
    124      *
    125      * @param  string $post_type
    126      * @return bool
    127     */
    128     protected function ignoringPostType($post_type)
    129     {
    130         return in_array($post_type, $this->ignoredPostTypes);
    131     }
     143    /**
     144     * Generate a label version of the given post ids post type
     145     *
     146     * @param int $postId
     147     *
     148     * @return string
     149    */
     150    protected function getPostTypeLabel(int $postId): string
     151    {
     152        return $this->formatLabel(get_post_type($postId));
     153    }
    132154
    133     /**
    134      * Ignore certain system generated post titles
    135      *
    136      * @param  string $title
    137      * @return bool
    138      */
    139     protected function ignoringPostTitle($title)
    140     {
    141         return in_array($title, $this->ignoredPostTitles);
    142     }
    143 
    144     /**
    145      * Generate a label version of the given post ids post type
    146      *
    147      * @param  integer $post_id
    148      * @return string
    149      */
    150     protected function getPostTypeLabel($post_id)
    151     {
    152         return $this->formatLabel(get_post_type($post_id));
    153     }
    154 
    155     protected function formatLabel($label)
    156     {
    157         return ucwords( str_replace(['_', '-'], ' ', $label) );
    158     }
     155    /**
     156     * @param string $label
     157     *
     158     * @return string
     159     */
     160    protected function formatLabel(string $label): string
     161    {
     162        return ucwords(str_replace(['_', '-'], ' ', $label));
     163    }
    159164}
  • logtivity/trunk/Services/Logtivity_Api.php

    r3246625 r3264602  
    2525class Logtivity_Api
    2626{
    27     /**
    28     * Option class to access the plugin settings
    29     *
    30     * @var object
    31     */
    32     protected $options;
     27    /**
     28    * Option class to access the plugin settings
     29    *
     30    * @var object
     31    */
     32    protected Logtivity_Options $options;
    3333
    34     /**
    35     * Should we wait to return the response from the API?
    36     *
    37      * @var boolean
    38     */
    39     public $waitForResponse = true;
     34    /**
     35    * Should we wait to return the response from the API?
     36    *
     37     * @var bool
     38    */
     39    public bool $waitForResponse = true;
    4040
    41     /**
    42     * Definitely don't wait for a response.
    43     *
    44      * @var boolean
    45     */
    46     public $asyncOverride = false;
     41    /**
     42    * Definitely don't wait for a response.
     43    *
     44     * @var bool
     45    */
     46    public bool $asyncOverride = false;
    4747
    48     /**
    49     * The API key for either the site or team
    50     *
    51     * @var string
    52     */
    53     public $api_key;
     48    /**
     49    * The API key for either the site or team
     50    *
     51    * @var string
     52    */
     53    public string $api_key = '';
    5454
    55     public function __construct()
    56     {
    57         $this->options = new Logtivity_Options;
    58     }
     55    public function __construct()
     56    {
     57        $this->options = new Logtivity_Options();
     58    }
    5959
    60     /**
    61      * Get the API URL for the Logtivity endpoint
    62     *
    63     * @return string
    64     */
    65     public function getEndpoint($endpoint)
    66     {
    67         return logtivity_get_api_url() . $endpoint;
    68     }
     60    /**
     61     * @param string $endpoint
     62    *
     63    * @return string
     64    */
     65    public function getEndpoint(string $endpoint): string
     66    {
     67        return logtivity_get_api_url() . $endpoint;
     68    }
    6969
    70     public function post($url, $body)
    71     {
    72         return $this->makeRequest($url, $body, 'POST');
    73     }
     70    public function post(string $url, array $body)
     71    {
     72        return $this->makeRequest($url, $body);
     73    }
    7474
    75     public function get($url, $body)
    76     {
    77         return $this->makeRequest($url, $body, 'GET');
    78     }
     75    public function get($url, $body)
     76    {
     77        return $this->makeRequest($url, $body, 'GET');
     78    }
    7979
    80     public function setApiKey($api_key)
    81     {
    82         $this->api_key = $api_key;
     80    /**
     81     * @param string $apikey
     82     *
     83     * @return $this
     84     */
     85    public function setApiKey(string $apikey): self
     86    {
     87        $this->api_key = $apikey;
    8388
    84         return $this;
    85     }
     89        return $this;
     90    }
    8691
    87     public function async()
    88     {
    89         $this->asyncOverride = true;
     92    /**
     93     * @return $this
     94     */
     95    public function async(): self
     96    {
     97        $this->asyncOverride = true;
    9098
    91         return $this;
    92     }
     99        return $this;
     100    }
    93101
    94     /**
    95      * Make a request to the Logtivity API
    96      *
    97      * @param  string $url
    98      * @param  array $body
    99      * @param  string $method
     102    /**
     103     * Make a request to the Logtivity API
    100104     *
    101      * @return mixed $response
    102      */
    103     public function makeRequest(string $url, array $body, string $method = 'POST')
    104     {
    105         if (!$this->api_key) {
    106             $this->api_key = logtivity_get_api_key();
    107         }
    108         if (!$this->api_key) {
    109             return null;
    110         }
     105     * @param string $url
     106     * @param array  $body
     107     * @param string $method
     108     *
     109     * @return mixed $response
     110     */
     111    public function makeRequest(string $url, array $body, string $method = 'POST'): ?string
     112    {
     113        $this->api_key = $this->api_key ?: logtivity_get_api_key();
     114        if ($this->options->urlHash() == false) {
     115            $this->options->update(['logtivity_url_hash' => md5(home_url())], false);
     116        }
    111117
    112         if (!$this->options->urlHash()) {
    113             $this->options->update(['logtivity_url_hash' => md5(home_url())], false);
    114         }
    115         if (logtivity_has_site_url_changed()) {
    116             return null;
    117         }
     118        if ($this->api_key && logtivity_has_site_url_changed() == false) {
     119            $shouldLogLatestResponse = $this->asyncOverride == false
     120                && ($this->waitForResponse || $this->options->shouldLogLatestResponse());
    118121
    119         $shouldLogLatestResponse = !$this->asyncOverride && ($this->waitForResponse || $this->options->shouldLogLatestResponse());
     122            $response = wp_remote_post($this->getEndpoint($url), [
     123                'method'      => $method,
     124                'timeout'     => ($shouldLogLatestResponse ? 6 : 0.01),
     125                'blocking'    => $shouldLogLatestResponse,
     126                'redirection' => 5,
     127                'httpversion' => '1.0',
     128                'headers'     => [
     129                    'Authorization' => 'Bearer ' . $this->api_key,
     130                ],
     131                'body'        => $body,
     132                'cookies'     => [],
     133            ]);
    120134
    121         $response = wp_remote_post($this->getEndpoint($url), [
    122             'method' => $method,
    123             'timeout'   => ( $shouldLogLatestResponse ? 6 : 0.01),
    124             'blocking'  => ( $shouldLogLatestResponse ? true : false),
    125             'redirection' => 5,
    126             'httpversion' => '1.0',
    127             'headers' => [
    128                 'Authorization' => 'Bearer '.$this->api_key
    129             ],
    130             'body' => $body,
    131             'cookies' => array()
    132         ]);
     135            $response = wp_remote_retrieve_body($response);
    133136
    134         $response = wp_remote_retrieve_body($response);
     137            if ($response) {
     138                if ($shouldLogLatestResponse && $this->notUpdatingWidgetInCustomizer() && $method === 'POST') {
     139                    $this->options->update([
     140                        'logtivity_latest_response' => [
     141                            'date'     => date('Y-m-d H:i:s'),
     142                            'response' => print_r($response, true),
     143                        ],
     144                    ],
     145                        false
     146                    );
    135147
    136         if (empty($response)) {
    137             return $response;
    138         }
     148                    update_option('logtivity_last_settings_check_in_at', ['date' => date('Y-m-d H:i:s')]);
    139149
    140         if ($shouldLogLatestResponse && $this->notUpdatingWidgetInCustomizer() && $method === 'POST') {
    141             $this->options->update([
    142                     'logtivity_latest_response' => [
    143                         'date' => date("Y-m-d H:i:s"),
    144                         'response' => print_r($response, true)
    145                     ]
    146                 ],
    147                 false
    148             );
     150                    $body = json_decode($response, true);
    149151
    150             update_option('logtivity_last_settings_check_in_at', ['date' => date("Y-m-d H:i:s")]);
     152                    $this->updateSettings($body);
     153                }
     154            }
     155        }
    151156
    152             $body = json_decode($response, true);
     157        return $response ?: null;
     158    }
    153159
    154             $this->updateSettings($body);
    155         }
     160    public function updateSettings($body)
     161    {
     162        if (isset($body['settings'])) {
     163            $this->options->update([
     164                'logtivity_global_disabled_logs'         => $body['settings']['disabled_logs'] ?? null,
     165                'logtivity_enable_white_label_mode'      => $body['settings']['enable_white_label_mode'] ?? null,
     166                'logtivity_disabled_error_levels'        => $body['settings']['disabled_error_levels'] ?? null,
     167                'logtivity_disable_error_logging'        => $body['settings']['disable_error_logging'] ?? null,
     168                'logtivity_hide_plugin_from_ui'          => $body['settings']['hide_plugin_from_ui'] ?? null,
     169                'logtivity_disable_default_logging'      => $body['settings']['disable_default_logging'] ?? null,
     170                'logtivity_enable_options_table_logging' => $body['settings']['enable_options_table_logging'] ?? null,
     171                'logtivity_enable_post_meta_logging'     => $body['settings']['enable_post_meta_logging'] ?? null,
     172                'logtivity_custom_plugin_name'           => $body['settings']['custom_plugin_name'] ?? null,
     173            ],
     174                false
     175            );
     176        }
     177    }
    156178
    157         return $response;
    158     }
     179    /**
     180     * You cannot call an extra update_option during a widget update so we make
     181     * sure not to log the most recent log response in this case.
     182     *
     183     * @return bool
     184     */
     185    private function notUpdatingWidgetInCustomizer(): bool
     186    {
     187        if (!isset($_POST['wp_customize'])) {
     188            return true;
     189        }
    159190
    160     public function updateSettings($body)
    161     {
    162         if (isset($body['settings'])) {
    163             $this->options->update([
    164                     'logtivity_global_disabled_logs' => $body['settings']['disabled_logs'] ?? null,
    165                     'logtivity_enable_white_label_mode' => $body['settings']['enable_white_label_mode'] ?? null,
    166                     'logtivity_disabled_error_levels' => $body['settings']['disabled_error_levels'] ?? null,
    167                     'logtivity_disable_error_logging' => $body['settings']['disable_error_logging'] ?? null,
    168                     'logtivity_hide_plugin_from_ui' => $body['settings']['hide_plugin_from_ui'] ?? null,
    169                     'logtivity_disable_default_logging' => $body['settings']['disable_default_logging'] ?? null,
    170                     'logtivity_enable_options_table_logging' => $body['settings']['enable_options_table_logging'] ?? null,
    171                     'logtivity_enable_post_meta_logging' => $body['settings']['enable_post_meta_logging'] ?? null,
    172                     'logtivity_custom_plugin_name' => $body['settings']['custom_plugin_name'] ?? null,
    173                 ],
    174                 false
    175             );
    176         }
    177     }
     191        if (!isset($_POST['action'])) {
     192            return true;
     193        }
    178194
    179     /**
    180      * You cannot call an extra update_option during a widget update so we make
    181      * sure not to log the most recent log response in this case.
    182      *
    183      * @return bool
    184      */
    185     private function notUpdatingWidgetInCustomizer()
    186     {
    187         if (!isset($_POST['wp_customize'])) {
    188             return true;
    189         }
    190 
    191         if (!isset($_POST['action'])) {
    192             return true;
    193         }
    194 
    195         return ! ($_POST['action'] === 'update-widget' && $_POST['wp_customize'] === 'on');
    196     }
     195        return !($_POST['action'] === 'update-widget' && $_POST['wp_customize'] === 'on');
     196    }
    197197}
  • logtivity/trunk/Services/Logtivity_Logger.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528class Logtivity_Logger extends Logtivity_Api
    2629{
     
    3235     * @var bool
    3336     */
    34     public $active = true;
    35 
    36     /**
    37      * The action for the given log
    38      *
    39      * @var string
    40      */
    41     public $action;
    42 
    43     /**
    44      * The context for the given log. Could be a post title, or plugin
    45      * name, or anything to help give this log some more context.
    46      *
    47      * @var string
    48      */
    49     public $context;
    50 
    51     /**
    52      * The post type, if relevant for a given log
    53      *
    54      * @var string
    55      */
    56     public $post_type;
    57 
    58     /**
    59      * The post ID, if relevant for a given log
    60      *
    61      * @var integer
    62      */
    63     public $post_id;
    64 
    65     /**
    66      * Extra info to pass to the log
    67      *
     37    public bool $active = true;
     38
     39    /**
     40     * @var ?string
     41     */
     42    public ?string $action = null;
     43
     44    /**
     45     * @var ?string
     46     */
     47    public ?string $context = null;
     48
     49    /**
     50     * @var ?string
     51     */
     52    public ?string $post_type = null;
     53
     54    /**
     55     * @var ?int
     56     */
     57    public ?int $post_id = null;
     58
     59    /**
    6860     * @var array
    6961     */
    70     public $meta = [];
    71 
    72     /**
    73      * Extra user meta to pass to the log
    74      *
     62    public array $meta = [];
     63
     64    /**
    7565     * @var array
    7666     */
    77     public $userMeta = [];
     67    public array $userMeta = [];
    7868
    7969    /**
    8070     * When storing a log, generally we want to do this asynchronously
    81      * and so we won't wait for a response from the API by default.
    82      *
    83      * @var boolean
    84      */
    85     public $waitForResponse = false;
    86 
    87     /**
    88      * Set the user and call the parent constructor
    89      */
    90     public function __construct($user_id = null)
    91     {
    92         $this->setUser($user_id);
     71     * so we won't wait for a response from the API by default.
     72     *
     73     * @var bool
     74     */
     75    public bool $waitForResponse = false;
     76
     77    /**
     78     * @param ?int $userId
     79     */
     80    public function __construct(?int $userId = null)
     81    {
     82        $this->setUser($userId);
    9383
    9484        parent::__construct();
     
    9888     * Way into class.
    9989     *
     90     * @param ?string $action
     91     * @param ?array  $meta
     92     * @param ?int    $userId
     93     *
     94     * @return ?mixed
     95     */
     96    public static function log(?string $action = null, ?array $meta = null, ?int $userId = null)
     97    {
     98        $logtivityLogger = new Logtivity_Logger($userId);
     99
     100        if ($action === null) {
     101            return new $logtivityLogger();
     102        }
     103
     104        $logtivityLogger->setAction($action);
     105
     106        if ($meta) {
     107            $key   = $meta['key'] ?? null;
     108            $value = $meta['value'] ?? null;
     109            if ($key && $value) {
     110                $logtivityLogger->addMeta($key, $value);
     111            }
     112        }
     113
     114        return $logtivityLogger->send();
     115    }
     116
     117    /**
    100118     * @param string $action
    101      * @param string $meta
    102      * @param string $user_id
    103      * @return Logtivity_Logger::send()
    104      */
    105     public static function log($action = null, $meta = null, $user_id = null)
    106     {
    107         $Logtivity_logger = new Logtivity_Logger($user_id);
    108 
    109         if (is_null($action)) {
    110 
    111             return new $Logtivity_logger;
    112 
    113         }
    114 
    115         $Logtivity_logger->setAction($action);
    116 
    117         if ($meta) {
    118             $Logtivity_logger->addMeta($meta['key'], $meta['value']);
    119         }
    120 
    121         return $Logtivity_logger->send();
    122     }
    123 
    124     /**
    125      * Set the action string before sending
    126      *
    127      * @param string
    128      */
    129     public function setAction($action)
     119     *
     120     * @return $this
     121     */
     122    public function setAction(string $action): self
    130123    {
    131124        $this->action = $action;
     
    135128
    136129    /**
    137      * Set the context string before sending.
    138      *
    139      * @param string
    140      */
    141     public function setContext($context)
     130     * @param string $context
     131     *
     132     * @return $this
     133     */
     134    public function setContext(string $context): self
    142135    {
    143136        $this->context = $context;
     
    147140
    148141    /**
    149      * Set the post_type string before sending.
    150      *
    151      * @param string
    152      */
    153     public function setPostType($post_type)
    154     {
    155         $this->post_type = $post_type;
    156 
    157         return $this;
    158     }
    159 
    160     /**
    161      * Set the post_id before sending.
    162      *
    163      * @param integer
    164      */
    165     public function setPostId($post_id)
    166     {
    167         $this->post_id = $post_id;
    168 
    169         return $this;
    170     }
    171 
    172     /**
    173      * Add to an array any additional information you would like to pass to this log.
    174      *
     142     * @param string $postType
     143     *
     144     * @return $this
     145     */
     146    public function setPostType(string $postType): self
     147    {
     148        $this->post_type = $postType;
     149
     150        return $this;
     151    }
     152
     153    /**
     154     * @param int $postId
     155     *
     156     * @return $this
     157     */
     158    public function setPostId(int $postId): self
     159    {
     160        $this->post_id = $postId;
     161
     162        return $this;
     163    }
     164
     165    /**
    175166     * @param string $key
    176167     * @param mixed  $value
    177      * @return $this
    178      */
    179     public function addMeta($key, $value)
     168     *
     169     * @return $this
     170     */
     171    public function addMeta(string $key, $value): self
    180172    {
    181173        $this->meta[] = [
     
    190182     * Add the meta if the first condition is true
    191183     *
    192      * @param boolean $condition
    193      * @param string  $key
    194      * @param mixed   $value
    195      */
    196     public function addMetaIf($condition, $key, $value)
     184     * @param ?bool  $condition
     185     * @param string $key
     186     * @param mixed  $value
     187     *
     188     * @return $this
     189     */
     190    public function addMetaIf(?bool $condition, string $key, $value): self
    197191    {
    198192        if ($condition) {
     
    208202     * @param string $key
    209203     * @param mixed  $value
    210      * @return $this
    211      */
    212     public function addUserMeta($key, $value)
     204     *
     205     * @return $this
     206     */
     207    public function addUserMeta(string $key, $value): self
    213208    {
    214209        $this->userMeta[$key] = $value;
     
    218213
    219214    /**
    220      * Should we wait and record the response from logtivity.
    221      *
    222      * @return $this
    223      */
    224     public function waitForResponse()
     215     * @return $this
     216     */
     217    public function waitForResponse(): self
    225218    {
    226219        $this->waitForResponse = true;
     
    230223
    231224    /**
    232      * Stop this instance of Logtivity_Logger from logging
    233      *
    234      * @return $this
    235      */
    236     public function stop()
     225     * @return $this
     226     */
     227    public function stop(): self
    237228    {
    238229        $this->active = false;
     
    260251
    261252    /**
    262      * Build the data array for storing the log
    263      *
    264253     * @return array
    265254     */
    266     protected function getData()
     255    protected function getData(): array
    267256    {
    268257        return [
     
    284273     * @return array
    285274     */
    286     public function getUserMeta()
     275    public function getUserMeta(): array
    287276    {
    288277        return (array)apply_filters('wp_logtivity_get_user_meta', $this->userMeta);
     
    294283     * @return array
    295284     */
    296     public function getMeta()
     285    public function getMeta(): array
    297286    {
    298287        return (array)apply_filters('wp_logtivity_get_meta', $this->meta);
     
    300289
    301290    /**
    302      * Maybe get the users profile link
    303      *
    304      * @return string|false
    305      */
    306     protected function maybeAddProfileLink()
    307     {
    308         if (!$this->options->shouldStoreProfileLink()) {
    309             return;
    310         }
    311 
    312         if (!$this->user->isLoggedIn()) {
    313             return;
    314         }
    315 
    316         $profileLink = $this->user->profileLink();
    317 
    318         if ($profileLink == '') {
    319             return null;
    320         }
    321 
    322         return $this->addUserMeta('Profile Link', $profileLink);
     291     * @return $this
     292     */
     293    protected function maybeAddProfileLink(): self
     294    {
     295        if (
     296            $this->options->shouldStoreProfileLink()
     297            && $this->user->isLoggedIn()
     298            && ($profileLink = $this->user->profileLink())
     299        ) {
     300            $this->addUserMeta('Profile Link', $profileLink);
     301        }
     302
     303        return $this;
    323304    }
    324305}
  • logtivity/trunk/Services/Logtivity_User_Logger_Trait.php

    r3246625 r3264602  
    2323 */
    2424
     25// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
     26// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
     27
    2528trait Logtivity_User_Logger_Trait
    2629{
    27     /**
    28      * Logtivity_Wp_User
    29      *
    30      * @var object
    31      */
    32     public $user;
    33    
    34     /**
    35      * Set the user for the current log instance
    36      *
    37      * @param integer $user_id
    38      */
    39     public function setUser($user_id = null)
    40     {
    41         $this->user = new Logtivity_Wp_User($user_id);
     30    /**
     31     * @var Logtivity_Wp_User
     32     */
     33    public Logtivity_Wp_User $user;
    4234
    43         return $this;
    44     }
     35    /**
     36     * @param ?int $userId
     37     *
     38     * @return $this
     39     */
     40    public function setUser(?int $userId = null): self
     41    {
     42        $this->user = new Logtivity_Wp_User($userId);
    4543
    46     /**
    47      * Protected function to get the User ID if the user is logged in
    48      *
    49      * @return mixed string|integer
    50      */
    51     protected function getUserID()
    52     {
    53         if (!$this->options->shouldStoreUserId()) {
    54             return;
    55         }
     44        return $this;
     45    }
    5646
    57         if (!$this->user->isLoggedIn()) {
    58             return;
    59         }
     47    /**
     48     * @return ?int
     49     */
     50    protected function getUserID(): ?int
     51    {
     52        if (
     53            $this->options->shouldStoreUserId()
     54            && $this->user->isLoggedIn()
     55        ) {
     56            return $this->user->id();
     57        }
    6058
    61         return $this->user->id();
    62     }
     59        return null;
     60    }
    6361
    64     /**
    65      * Maybe get the users IP address
    66      *
    67      * @return string|false
    68      */
    69     protected function maybeGetUsersIp()
    70     {
    71         if (!$this->options->shouldStoreIp()) {
    72             return;
    73         }
     62    /**
     63     * @return ?string
     64     */
     65    protected function maybeGetUsersIp(): ?string
     66    {
     67        if ($this->options->shouldStoreIp()) {
     68            $ipAddress = filter_var(
     69                ($_SERVER['HTTP_CLIENT_IP'] ?? '')
     70                    ?: ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? '')
     71                    ?: $_SERVER['REMOTE_ADDR'] ?? '',
     72                FILTER_VALIDATE_IP
     73            );
    7474
    75         if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
    76             //check ip from share internet
    77             return $_SERVER['HTTP_CLIENT_IP'];
    78         } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
    79             //to check ip is pass from proxy
    80             return $_SERVER['HTTP_X_FORWARDED_FOR'];
    81         } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
    82             return $_SERVER['REMOTE_ADDR'];
    83         }
    84     }
     75            return $ipAddress ?: null;
     76        }
    8577
    86     /**
    87      * Maybe get the users username
    88      *
    89      * @return string|false
    90      */
    91     protected function maybeGetUsersUsername()
    92     {
    93         if (!$this->options->shouldStoreUsername()) {
    94             return null;
    95         }
     78        return null;
     79    }
    9680
    97         if (!$this->user->isLoggedIn()) {
    98             return;
    99         }
     81    /**
     82     * @return ?string
     83     */
     84    protected function maybeGetUsersUsername(): ?string
     85    {
     86        if (
     87            $this->options->shouldStoreUsername()
     88            && $this->user->isLoggedIn()
     89        ) {
     90            return $this->user->userLogin();
     91        }
    10092
    101         return $this->user->userLogin();
    102     }
     93        return null;
     94    }
    10395}
  • logtivity/trunk/logtivity.php

    r3251888 r3264602  
    55 * Plugin URI:  https://logtivity.io
    66 * Description: Record activity logs and errors logs across all your WordPress sites.
    7  * Version:     3.1.6
     7 * Version:     3.1.7
    88 * Author:      Logtivity
    99 * Text Domain: logtivity
     
    4343     * @var string
    4444     */
    45     protected string $version = '3.1.6';
     45    protected string $version = '3.1.7';
    4646
    4747    /**
     
    129129        $this->loadDependencies();
    130130
    131         register_activation_hook(__FILE__, [$this, 'activated']);
     131        add_action('plugins_loaded', [$this, 'updateCheck']);
    132132
    133133        add_action('upgrader_process_complete', [$this, 'upgradeProcessComplete'], 10, 2);
     
    138138
    139139        add_filter('plugin_action_links_' . plugin_basename(__FILE__), [$this, 'addSettingsLinkFromPluginsPage']);
    140     }
    141 
    142     public function loadDependencies()
     140
     141        register_activation_hook(__FILE__, [$this, 'activated']);
     142    }
     143
     144    /**
     145     * @return void
     146     */
     147    public function loadDependencies(): void
    143148    {
    144149        foreach ($this->dependencies as $filePath) {
     
    157162    }
    158163
    159     public function loadFile($filePath)
     164    /**
     165     * @param string $filePath
     166     *
     167     * @return void
     168     */
     169    public function loadFile(string $filePath): void
    160170    {
    161171        require_once plugin_dir_path(__FILE__) . $filePath . '.php';
     
    172182    }
    173183
    174     public function maybeLoadLogClasses()
     184    /**
     185     * @return void
     186     */
     187    public function maybeLoadLogClasses(): void
    175188    {
    176189        foreach ($this->logClasses as $filePath) {
     
    179192    }
    180193
    181     public function loadIntegrationDependencies()
     194    /**
     195     * @return void
     196     */
     197    public function loadIntegrationDependencies(): void
    182198    {
    183199        foreach ($this->integrationDependencies as $key => $value) {
     
    190206    }
    191207
    192     public static function log($action = null, $meta = null, $user_id = null)
    193     {
    194         return Logtivity_Logger::log($action, $meta, $user_id);
     208    /**
     209     * @param ?string $action
     210     * @param ?array  $meta
     211     * @param ?int    $userId
     212     *
     213     * @return ?mixed
     214     */
     215    public static function log(?string $action = null, ?array $meta = null, ?int $userId = null)
     216    {
     217        return Logtivity_Logger::log($action, $meta, $userId);
    195218    }
    196219
     
    206229
    207230    /**
    208      * @param $upgraderObject
    209      * @param $options
    210      *
    211      * @return null|void
    212      */
    213     public function upgradeProcessComplete($upgraderObject, $options)
     231     * Review updates based on version
     232     *
     233     * @return void
     234     */
     235    public function updateCheck(): void
     236    {
     237        $currentVersion = get_option('logtivity_version');
     238
     239        if (version_compare($currentVersion, '3.1.7', '<')) {
     240            static::checkCapabilities();
     241        }
     242
     243        update_option('logtivity_version', $this->version);
     244    }
     245
     246    /**
     247     * @param WP_Upgrader $upgraderObject
     248     * @param array       $options
     249     *
     250     * @return void
     251     */
     252    public function upgradeProcessComplete(WP_Upgrader $upgraderObject, array $options): void
    214253    {
    215254        $type   = $options['type'] ?? null;
     
    271310
    272311    /**
     312     * Custom capabilities added prior to v3.1.7
     313     *
    273314     * @return void
    274315     */
    275316    public static function checkCapabilities(): void
    276317    {
    277         /** @var WP_User[] $users */
    278         $users = get_users();
    279         $caps  = [];
    280         foreach ($users as $user) {
    281             foreach ($user->roles as $role) {
    282                 $r    = get_role($role);
    283                 $caps = array_merge($caps, $r->capabilities ?: []);
    284             }
    285         }
    286 
    287         if (isset($caps[static::ACCESS_LOGS]) == false || isset($caps[static::ACCESS_SETTINGS]) == false) {
     318        $capabilities = array_filter(
     319            array_keys(logtivity_get_capabilities()),
     320            function (string $capability): bool {
     321                return in_array($capability, [Logtivity::ACCESS_LOGS, Logtivity::ACCESS_SETTINGS]);
     322            }
     323        );
     324
     325        if ($capabilities == false) {
    288326            // Make sure at least admins can access us
    289327            if ($role = get_role('administrator')) {
  • logtivity/trunk/readme.txt

    r3251888 r3264602  
    55Requires at least: 4.7
    66Tested up to: 6.7.2
    7 Stable tag: 3.1.6
     7Stable tag: 3.1.7
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    263263== Changelog ==
    264264
    265 = 3.1.6 =
    266 
    267 * Update: Automatically check and ensure capabilities exist
     265= 3.1.7 =
     266
     267* Check for installed custom capabilities
     268* Improved input sanitization
     269
     270_Release Date - Thursday, March 27 2025_
    268271
    269272= 3.1.5 =
     
    271274* Add: new capabilities - 'view logs', 'view log settings'
    272275
    273 _Release Date - Pending_
     276_Release Date - Tuesday, February 25th 2025_
    274277
    275278= 3.1.4 =
Note: See TracChangeset for help on using the changeset viewer.