Changeset 3264602
- Timestamp:
- 03/31/2025 04:55:15 PM (11 months ago)
- Location:
- logtivity
- Files:
-
- 20 edited
- 1 copied
-
tags/3.1.7 (copied) (copied from logtivity/trunk)
-
tags/3.1.7/Admin/Logtivity_Admin.php (modified) (1 diff)
-
tags/3.1.7/Errors/Logtivity_Error_Logger.php (modified) (1 diff)
-
tags/3.1.7/Helpers/Helpers.php (modified) (2 diffs)
-
tags/3.1.7/Logs/Core/Logtivity_Post.php (modified) (1 diff)
-
tags/3.1.7/Logs/Logtivity_Abstract_Logger.php (modified) (1 diff)
-
tags/3.1.7/Services/Logtivity_Api.php (modified) (1 diff)
-
tags/3.1.7/Services/Logtivity_Logger.php (modified) (13 diffs)
-
tags/3.1.7/Services/Logtivity_User_Logger_Trait.php (modified) (1 diff)
-
tags/3.1.7/logtivity.php (modified) (10 diffs)
-
tags/3.1.7/readme.txt (modified) (3 diffs)
-
trunk/Admin/Logtivity_Admin.php (modified) (1 diff)
-
trunk/Errors/Logtivity_Error_Logger.php (modified) (1 diff)
-
trunk/Helpers/Helpers.php (modified) (2 diffs)
-
trunk/Logs/Core/Logtivity_Post.php (modified) (1 diff)
-
trunk/Logs/Logtivity_Abstract_Logger.php (modified) (1 diff)
-
trunk/Services/Logtivity_Api.php (modified) (1 diff)
-
trunk/Services/Logtivity_Logger.php (modified) (13 diffs)
-
trunk/Services/Logtivity_User_Logger_Trait.php (modified) (1 diff)
-
trunk/logtivity.php (modified) (10 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
logtivity/tags/3.1.7/Admin/Logtivity_Admin.php
r3251888 r3264602 102 102 public function registerOptionsPage() 103 103 { 104 Logtivity::checkCapabilities();105 106 104 if (!apply_filters('logtivity_hide_from_menu', false)) { 107 105 add_menu_page( -
logtivity/tags/3.1.7/Errors/Logtivity_Error_Logger.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 class Logtivity_Error_Logger extends Logtivity_Api 26 29 { 27 use Logtivity_User_Logger_Trait;30 use Logtivity_User_Logger_Trait; 28 31 29 protected $active = true; 32 /** 33 * @var bool 34 */ 35 protected bool $active = true; 30 36 31 protected $error; 37 /** 38 * @var array 39 */ 40 protected array $error = []; 32 41 33 protected static $recordedErrors = []; 42 /** 43 * @var string[] 44 */ 45 protected static array $recordedErrors = []; 34 46 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; 38 53 39 $this->setUser();54 $this->setUser(); 40 55 41 parent::__construct();42 }56 parent::__construct(); 57 } 43 58 44 public function stop() 45 { 46 $this->active = false; 59 /** 60 * @return $this 61 */ 62 public function stop(): self 63 { 64 $this->active = false; 47 65 48 return $this;49 }66 return $this; 67 } 50 68 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']; 56 77 57 self::$recordedErrors[] = $this->error['message'];78 do_action('wp_logtivity_error_logger_instance', $this); 58 79 59 do_action('wp_logtivity_error_logger_instance', $this); 80 if ($this->active) { 81 $response = $this->async()->makeRequest('/errors/store', $this->getData()); 82 } 83 } 60 84 61 if (!$this->active) { 62 return; 63 } 85 return $response ?? null; 86 } 64 87 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']); 71 94 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 } 75 117 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(); 98 127 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 } 102 131 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 } 106 137 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 } 112 145 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; 117 155 118 private function getCurrentUrl() 119 { 120 if (!isset($_SERVER['HTTP_HOST'])) { 121 return; 122 } 156 $protocol = $ssl ? 'https://' : 'http://'; 123 157 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'] ?? ''); 125 159 126 $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 127 128 if ($url == $protocol) { 129 return; 130 } 160 $url = sanitize_url($protocol . $host . $path); 131 161 132 return $url; 133 } 162 if ($url != $protocol) { 163 return $url; 164 } 165 } 134 166 135 private function getErrorLevel($level) 136 { 137 $errorlevels = logtivity_get_error_levels(); 167 return null; 168 } 138 169 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 } 141 181 } -
logtivity/tags/3.1.7/Helpers/Helpers.php
r3246625 r3264602 120 120 $hash = (new Logtivity_Options())->urlHash(); 121 121 122 if (!$hash) { 123 return false; 124 } 125 126 return $hash !== md5(home_url()); 122 return $hash != md5(home_url()); 127 123 } 128 124 … … 145 141 return $errorLevels; 146 142 } 143 144 /** 145 * Get all known capabilities 146 * 147 * @return array 148 */ 149 function 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 23 23 */ 24 24 25 // phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols 26 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 27 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 28 25 29 class Logtivity_Post extends Logtivity_Abstract_Logger 26 30 { 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 } 202 227 } 203 228 204 $Logtivity_Post = new Logtivity_Post;229 new Logtivity_Post(); -
logtivity/tags/3.1.7/Logs/Logtivity_Abstract_Logger.php
r3246625 r3264602 25 25 abstract class Logtivity_Abstract_Logger 26 26 { 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 ]; 28 38 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 ]; 43 45 44 public function __construct() 45 { 46 $this->registerHooks(); 47 } 46 /** 47 * @var string[] 48 */ 49 protected array $ignoredPostStatuses = ['trash']; 48 50 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 } 60 55 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 } 64 68 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(); 68 77 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); 72 80 73 return false; 74 } 81 $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp(); 75 82 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 } 84 85 85 if (!$date) { 86 return false; 87 } 86 return false; 87 } 88 88 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); 91 98 92 $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp(); 99 } catch (Throwable $e) { 100 return new DateTime('1970-01-01'); 101 } 102 } 93 103 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 } 96 117 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 } 107 130 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 } 120 142 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 } 132 154 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 } 159 164 } -
logtivity/tags/3.1.7/Services/Logtivity_Api.php
r3246625 r3264602 25 25 class Logtivity_Api 26 26 { 27 /**28 * Option class to access the plugin settings29 *30 * @var object31 */32 protected$options;27 /** 28 * Option class to access the plugin settings 29 * 30 * @var object 31 */ 32 protected Logtivity_Options $options; 33 33 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; 40 40 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; 47 47 48 /**49 * The API key for either the site or team50 *51 * @var string52 */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 = ''; 54 54 55 public function __construct()56 {57 $this->options = new Logtivity_Options;58 }55 public function __construct() 56 { 57 $this->options = new Logtivity_Options(); 58 } 59 59 60 /**61 * Get the API URL for the Logtivityendpoint62 *63 * @return string64 */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 } 69 69 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 } 74 74 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 } 79 79 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; 83 88 84 return $this;85 }89 return $this; 90 } 86 91 87 public function async() 88 { 89 $this->asyncOverride = true; 92 /** 93 * @return $this 94 */ 95 public function async(): self 96 { 97 $this->asyncOverride = true; 90 98 91 return $this;92 }99 return $this; 100 } 93 101 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 100 104 * 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 } 111 117 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()); 118 121 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 ]); 120 134 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); 133 136 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 ); 135 147 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')]); 139 149 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); 149 151 150 update_option('logtivity_last_settings_check_in_at', ['date' => date("Y-m-d H:i:s")]); 152 $this->updateSettings($body); 153 } 154 } 155 } 151 156 152 $body = json_decode($response, true); 157 return $response ?: null; 158 } 153 159 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 } 156 178 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 } 159 190 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 } 178 194 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 } 197 197 } -
logtivity/tags/3.1.7/Services/Logtivity_Logger.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 class Logtivity_Logger extends Logtivity_Api 26 29 { … … 32 35 * @var bool 33 36 */ 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 /** 68 60 * @var array 69 61 */ 70 public $meta = []; 71 72 /** 73 * Extra user meta to pass to the log 74 * 62 public array $meta = []; 63 64 /** 75 65 * @var array 76 66 */ 77 public $userMeta = [];67 public array $userMeta = []; 78 68 79 69 /** 80 70 * When storing a log, generally we want to do this asynchronously 81 * andso we won't wait for a response from the API by default.82 * 83 * @var bool ean84 */ 85 public $waitForResponse = false;86 87 /** 88 * Set the user and call the parent constructor89 */ 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); 93 83 94 84 parent::__construct(); … … 98 88 * Way into class. 99 89 * 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 /** 100 118 * @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 130 123 { 131 124 $this->action = $action; … … 135 128 136 129 /** 137 * Set the context string before sending.138 * 139 * @ param string140 */ 141 public function setContext( $context)130 * @param string $context 131 * 132 * @return $this 133 */ 134 public function setContext(string $context): self 142 135 { 143 136 $this->context = $context; … … 147 140 148 141 /** 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 /** 175 166 * @param string $key 176 167 * @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 180 172 { 181 173 $this->meta[] = [ … … 190 182 * Add the meta if the first condition is true 191 183 * 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 197 191 { 198 192 if ($condition) { … … 208 202 * @param string $key 209 203 * @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 213 208 { 214 209 $this->userMeta[$key] = $value; … … 218 213 219 214 /** 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 225 218 { 226 219 $this->waitForResponse = true; … … 230 223 231 224 /** 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 237 228 { 238 229 $this->active = false; … … 260 251 261 252 /** 262 * Build the data array for storing the log263 *264 253 * @return array 265 254 */ 266 protected function getData() 255 protected function getData(): array 267 256 { 268 257 return [ … … 284 273 * @return array 285 274 */ 286 public function getUserMeta() 275 public function getUserMeta(): array 287 276 { 288 277 return (array)apply_filters('wp_logtivity_get_user_meta', $this->userMeta); … … 294 283 * @return array 295 284 */ 296 public function getMeta() 285 public function getMeta(): array 297 286 { 298 287 return (array)apply_filters('wp_logtivity_get_meta', $this->meta); … … 300 289 301 290 /** 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; 323 304 } 324 305 } -
logtivity/tags/3.1.7/Services/Logtivity_User_Logger_Trait.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 trait Logtivity_User_Logger_Trait 26 29 { 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; 42 34 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); 45 43 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 } 56 46 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 } 60 58 61 return $this->user->id();62 }59 return null; 60 } 63 61 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 ); 74 74 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 } 85 77 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 } 96 80 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 } 100 92 101 return $this->user->userLogin();102 }93 return null; 94 } 103 95 } -
logtivity/tags/3.1.7/logtivity.php
r3251888 r3264602 5 5 * Plugin URI: https://logtivity.io 6 6 * Description: Record activity logs and errors logs across all your WordPress sites. 7 * Version: 3.1. 67 * Version: 3.1.7 8 8 * Author: Logtivity 9 9 * Text Domain: logtivity … … 43 43 * @var string 44 44 */ 45 protected string $version = '3.1. 6';45 protected string $version = '3.1.7'; 46 46 47 47 /** … … 129 129 $this->loadDependencies(); 130 130 131 register_activation_hook(__FILE__, [$this, 'activated']);131 add_action('plugins_loaded', [$this, 'updateCheck']); 132 132 133 133 add_action('upgrader_process_complete', [$this, 'upgradeProcessComplete'], 10, 2); … … 138 138 139 139 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 143 148 { 144 149 foreach ($this->dependencies as $filePath) { … … 157 162 } 158 163 159 public function loadFile($filePath) 164 /** 165 * @param string $filePath 166 * 167 * @return void 168 */ 169 public function loadFile(string $filePath): void 160 170 { 161 171 require_once plugin_dir_path(__FILE__) . $filePath . '.php'; … … 172 182 } 173 183 174 public function maybeLoadLogClasses() 184 /** 185 * @return void 186 */ 187 public function maybeLoadLogClasses(): void 175 188 { 176 189 foreach ($this->logClasses as $filePath) { … … 179 192 } 180 193 181 public function loadIntegrationDependencies() 194 /** 195 * @return void 196 */ 197 public function loadIntegrationDependencies(): void 182 198 { 183 199 foreach ($this->integrationDependencies as $key => $value) { … … 190 206 } 191 207 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); 195 218 } 196 219 … … 206 229 207 230 /** 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 214 253 { 215 254 $type = $options['type'] ?? null; … … 271 310 272 311 /** 312 * Custom capabilities added prior to v3.1.7 313 * 273 314 * @return void 274 315 */ 275 316 public static function checkCapabilities(): void 276 317 { 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) { 288 326 // Make sure at least admins can access us 289 327 if ($role = get_role('administrator')) { -
logtivity/tags/3.1.7/readme.txt
r3251888 r3264602 5 5 Requires at least: 4.7 6 6 Tested up to: 6.7.2 7 Stable tag: 3.1. 67 Stable tag: 3.1.7 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 263 263 == Changelog == 264 264 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_ 268 271 269 272 = 3.1.5 = … … 271 274 * Add: new capabilities - 'view logs', 'view log settings' 272 275 273 _Release Date - Pending_276 _Release Date - Tuesday, February 25th 2025_ 274 277 275 278 = 3.1.4 = -
logtivity/trunk/Admin/Logtivity_Admin.php
r3251888 r3264602 102 102 public function registerOptionsPage() 103 103 { 104 Logtivity::checkCapabilities();105 106 104 if (!apply_filters('logtivity_hide_from_menu', false)) { 107 105 add_menu_page( -
logtivity/trunk/Errors/Logtivity_Error_Logger.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 class Logtivity_Error_Logger extends Logtivity_Api 26 29 { 27 use Logtivity_User_Logger_Trait;30 use Logtivity_User_Logger_Trait; 28 31 29 protected $active = true; 32 /** 33 * @var bool 34 */ 35 protected bool $active = true; 30 36 31 protected $error; 37 /** 38 * @var array 39 */ 40 protected array $error = []; 32 41 33 protected static $recordedErrors = []; 42 /** 43 * @var string[] 44 */ 45 protected static array $recordedErrors = []; 34 46 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; 38 53 39 $this->setUser();54 $this->setUser(); 40 55 41 parent::__construct();42 }56 parent::__construct(); 57 } 43 58 44 public function stop() 45 { 46 $this->active = false; 59 /** 60 * @return $this 61 */ 62 public function stop(): self 63 { 64 $this->active = false; 47 65 48 return $this;49 }66 return $this; 67 } 50 68 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']; 56 77 57 self::$recordedErrors[] = $this->error['message'];78 do_action('wp_logtivity_error_logger_instance', $this); 58 79 59 do_action('wp_logtivity_error_logger_instance', $this); 80 if ($this->active) { 81 $response = $this->async()->makeRequest('/errors/store', $this->getData()); 82 } 83 } 60 84 61 if (!$this->active) { 62 return; 63 } 85 return $response ?? null; 86 } 64 87 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']); 71 94 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 } 75 117 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(); 98 127 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 } 102 131 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 } 106 137 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 } 112 145 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; 117 155 118 private function getCurrentUrl() 119 { 120 if (!isset($_SERVER['HTTP_HOST'])) { 121 return; 122 } 156 $protocol = $ssl ? 'https://' : 'http://'; 123 157 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'] ?? ''); 125 159 126 $url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 127 128 if ($url == $protocol) { 129 return; 130 } 160 $url = sanitize_url($protocol . $host . $path); 131 161 132 return $url; 133 } 162 if ($url != $protocol) { 163 return $url; 164 } 165 } 134 166 135 private function getErrorLevel($level) 136 { 137 $errorlevels = logtivity_get_error_levels(); 167 return null; 168 } 138 169 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 } 141 181 } -
logtivity/trunk/Helpers/Helpers.php
r3246625 r3264602 120 120 $hash = (new Logtivity_Options())->urlHash(); 121 121 122 if (!$hash) { 123 return false; 124 } 125 126 return $hash !== md5(home_url()); 122 return $hash != md5(home_url()); 127 123 } 128 124 … … 145 141 return $errorLevels; 146 142 } 143 144 /** 145 * Get all known capabilities 146 * 147 * @return array 148 */ 149 function 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 23 23 */ 24 24 25 // phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols 26 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 27 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 28 25 29 class Logtivity_Post extends Logtivity_Abstract_Logger 26 30 { 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 } 202 227 } 203 228 204 $Logtivity_Post = new Logtivity_Post;229 new Logtivity_Post(); -
logtivity/trunk/Logs/Logtivity_Abstract_Logger.php
r3246625 r3264602 25 25 abstract class Logtivity_Abstract_Logger 26 26 { 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 ]; 28 38 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 ]; 43 45 44 public function __construct() 45 { 46 $this->registerHooks(); 47 } 46 /** 47 * @var string[] 48 */ 49 protected array $ignoredPostStatuses = ['trash']; 48 50 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 } 60 55 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 } 64 68 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(); 68 77 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); 72 80 73 return false; 74 } 81 $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp(); 75 82 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 } 84 85 85 if (!$date) { 86 return false; 87 } 86 return false; 87 } 88 88 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); 91 98 92 $diffInSeconds = $now->getTimestamp() - $lastLogged->getTimestamp(); 99 } catch (Throwable $e) { 100 return new DateTime('1970-01-01'); 101 } 102 } 93 103 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 } 96 117 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 } 107 130 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 } 120 142 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 } 132 154 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 } 159 164 } -
logtivity/trunk/Services/Logtivity_Api.php
r3246625 r3264602 25 25 class Logtivity_Api 26 26 { 27 /**28 * Option class to access the plugin settings29 *30 * @var object31 */32 protected$options;27 /** 28 * Option class to access the plugin settings 29 * 30 * @var object 31 */ 32 protected Logtivity_Options $options; 33 33 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; 40 40 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; 47 47 48 /**49 * The API key for either the site or team50 *51 * @var string52 */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 = ''; 54 54 55 public function __construct()56 {57 $this->options = new Logtivity_Options;58 }55 public function __construct() 56 { 57 $this->options = new Logtivity_Options(); 58 } 59 59 60 /**61 * Get the API URL for the Logtivityendpoint62 *63 * @return string64 */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 } 69 69 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 } 74 74 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 } 79 79 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; 83 88 84 return $this;85 }89 return $this; 90 } 86 91 87 public function async() 88 { 89 $this->asyncOverride = true; 92 /** 93 * @return $this 94 */ 95 public function async(): self 96 { 97 $this->asyncOverride = true; 90 98 91 return $this;92 }99 return $this; 100 } 93 101 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 100 104 * 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 } 111 117 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()); 118 121 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 ]); 120 134 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); 133 136 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 ); 135 147 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')]); 139 149 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); 149 151 150 update_option('logtivity_last_settings_check_in_at', ['date' => date("Y-m-d H:i:s")]); 152 $this->updateSettings($body); 153 } 154 } 155 } 151 156 152 $body = json_decode($response, true); 157 return $response ?: null; 158 } 153 159 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 } 156 178 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 } 159 190 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 } 178 194 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 } 197 197 } -
logtivity/trunk/Services/Logtivity_Logger.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 class Logtivity_Logger extends Logtivity_Api 26 29 { … … 32 35 * @var bool 33 36 */ 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 /** 68 60 * @var array 69 61 */ 70 public $meta = []; 71 72 /** 73 * Extra user meta to pass to the log 74 * 62 public array $meta = []; 63 64 /** 75 65 * @var array 76 66 */ 77 public $userMeta = [];67 public array $userMeta = []; 78 68 79 69 /** 80 70 * When storing a log, generally we want to do this asynchronously 81 * andso we won't wait for a response from the API by default.82 * 83 * @var bool ean84 */ 85 public $waitForResponse = false;86 87 /** 88 * Set the user and call the parent constructor89 */ 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); 93 83 94 84 parent::__construct(); … … 98 88 * Way into class. 99 89 * 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 /** 100 118 * @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 130 123 { 131 124 $this->action = $action; … … 135 128 136 129 /** 137 * Set the context string before sending.138 * 139 * @ param string140 */ 141 public function setContext( $context)130 * @param string $context 131 * 132 * @return $this 133 */ 134 public function setContext(string $context): self 142 135 { 143 136 $this->context = $context; … … 147 140 148 141 /** 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 /** 175 166 * @param string $key 176 167 * @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 180 172 { 181 173 $this->meta[] = [ … … 190 182 * Add the meta if the first condition is true 191 183 * 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 197 191 { 198 192 if ($condition) { … … 208 202 * @param string $key 209 203 * @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 213 208 { 214 209 $this->userMeta[$key] = $value; … … 218 213 219 214 /** 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 225 218 { 226 219 $this->waitForResponse = true; … … 230 223 231 224 /** 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 237 228 { 238 229 $this->active = false; … … 260 251 261 252 /** 262 * Build the data array for storing the log263 *264 253 * @return array 265 254 */ 266 protected function getData() 255 protected function getData(): array 267 256 { 268 257 return [ … … 284 273 * @return array 285 274 */ 286 public function getUserMeta() 275 public function getUserMeta(): array 287 276 { 288 277 return (array)apply_filters('wp_logtivity_get_user_meta', $this->userMeta); … … 294 283 * @return array 295 284 */ 296 public function getMeta() 285 public function getMeta(): array 297 286 { 298 287 return (array)apply_filters('wp_logtivity_get_meta', $this->meta); … … 300 289 301 290 /** 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; 323 304 } 324 305 } -
logtivity/trunk/Services/Logtivity_User_Logger_Trait.php
r3246625 r3264602 23 23 */ 24 24 25 // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace 26 // phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps 27 25 28 trait Logtivity_User_Logger_Trait 26 29 { 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; 42 34 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); 45 43 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 } 56 46 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 } 60 58 61 return $this->user->id();62 }59 return null; 60 } 63 61 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 ); 74 74 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 } 85 77 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 } 96 80 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 } 100 92 101 return $this->user->userLogin();102 }93 return null; 94 } 103 95 } -
logtivity/trunk/logtivity.php
r3251888 r3264602 5 5 * Plugin URI: https://logtivity.io 6 6 * Description: Record activity logs and errors logs across all your WordPress sites. 7 * Version: 3.1. 67 * Version: 3.1.7 8 8 * Author: Logtivity 9 9 * Text Domain: logtivity … … 43 43 * @var string 44 44 */ 45 protected string $version = '3.1. 6';45 protected string $version = '3.1.7'; 46 46 47 47 /** … … 129 129 $this->loadDependencies(); 130 130 131 register_activation_hook(__FILE__, [$this, 'activated']);131 add_action('plugins_loaded', [$this, 'updateCheck']); 132 132 133 133 add_action('upgrader_process_complete', [$this, 'upgradeProcessComplete'], 10, 2); … … 138 138 139 139 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 143 148 { 144 149 foreach ($this->dependencies as $filePath) { … … 157 162 } 158 163 159 public function loadFile($filePath) 164 /** 165 * @param string $filePath 166 * 167 * @return void 168 */ 169 public function loadFile(string $filePath): void 160 170 { 161 171 require_once plugin_dir_path(__FILE__) . $filePath . '.php'; … … 172 182 } 173 183 174 public function maybeLoadLogClasses() 184 /** 185 * @return void 186 */ 187 public function maybeLoadLogClasses(): void 175 188 { 176 189 foreach ($this->logClasses as $filePath) { … … 179 192 } 180 193 181 public function loadIntegrationDependencies() 194 /** 195 * @return void 196 */ 197 public function loadIntegrationDependencies(): void 182 198 { 183 199 foreach ($this->integrationDependencies as $key => $value) { … … 190 206 } 191 207 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); 195 218 } 196 219 … … 206 229 207 230 /** 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 214 253 { 215 254 $type = $options['type'] ?? null; … … 271 310 272 311 /** 312 * Custom capabilities added prior to v3.1.7 313 * 273 314 * @return void 274 315 */ 275 316 public static function checkCapabilities(): void 276 317 { 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) { 288 326 // Make sure at least admins can access us 289 327 if ($role = get_role('administrator')) { -
logtivity/trunk/readme.txt
r3251888 r3264602 5 5 Requires at least: 4.7 6 6 Tested up to: 6.7.2 7 Stable tag: 3.1. 67 Stable tag: 3.1.7 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 263 263 == Changelog == 264 264 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_ 268 271 269 272 = 3.1.5 = … … 271 274 * Add: new capabilities - 'view logs', 'view log settings' 272 275 273 _Release Date - Pending_276 _Release Date - Tuesday, February 25th 2025_ 274 277 275 278 = 3.1.4 =
Note: See TracChangeset
for help on using the changeset viewer.