array( 'login_succeeded', 'logout', 'login_failed', 'user_created', 'user_deleted', 'user_role_changed', 'password_changed' ), 'content' => array( 'post_created', 'post_updated', 'post_trashed', 'post_restored', 'post_deleted' ), 'plugins' => array( 'plugin_installed', 'plugin_activated', 'plugin_deactivated', 'plugin_updated', 'plugin_deleted' ), 'themes' => array( 'theme_installed', 'theme_switched', 'theme_updated', 'theme_deleted' ), 'wordpress' => array( 'wordpress_updated' ), 'adminwatch' => array( 'settings_updated' ), ); /** Category labels for filters and badges. */ public static function categories() { return array( 'users' => __( 'Users & Access', 'adminwatch-central' ), 'content' => __( 'Content', 'adminwatch-central' ), 'plugins' => __( 'Plugins', 'adminwatch-central' ), 'themes' => __( 'Themes', 'adminwatch-central' ), 'wordpress' => __( 'WordPress', 'adminwatch-central' ), 'adminwatch' => __( 'Admin Watch', 'adminwatch-central' ), ); } /** Return a fixed event definition; unknown events cannot be persisted. */ public static function definition( $type ) { foreach ( self::GROUPS as $category => $types ) { if ( in_array( $type, $types, true ) ) { $fields = array(); if ( 'user_role_changed' === $type ) { $fields = array( 'old_roles', 'new_roles' ); } elseif ( in_array( $type, array( 'plugin_updated', 'theme_updated', 'wordpress_updated' ), true ) ) { $fields = array( 'old_version', 'new_version' ); } elseif ( 'theme_switched' === $type ) { $fields = array( 'old_theme' ); } elseif ( 'settings_updated' === $type ) { $fields = array( 'monitor_404s', 'retention_days', 'delete_data', 'monitor_activity', 'activity_retention_days', 'record_failed_logins' ); } if ( 'plugins' === $category ) { $fields[] = 'plugin_file'; } elseif ( 'themes' === $category ) { $fields[] = 'theme_slug'; } return array( 'category' => $category, 'severity' => 'login_failed' === $type || 'user_role_changed' === $type ? 'attention' : ( in_array( $category, array( 'plugins', 'themes', 'wordpress' ), true ) ? 'notice' : 'info' ), 'fields' => $fields ); } } return null; } /** Match translated fixed message wording without storing or searching rendered HTML/JSON. */ public static function search_types( $search ) { $matches = array(); foreach ( self::GROUPS as $types ) { foreach ( $types as $type ) { $row = (object) array( 'event_type' => $type, 'actor_user_id' => 0, 'object_type' => '', 'object_id' => 0, 'object_label' => '' ); $template = str_replace( self::user_label( 0 ), '', self::message( $row ) ); if ( false !== stripos( $template, $search ) ) { $matches[] = $type; } } } return $matches; } /** Resolve account labels without storing duplicate personal names. */ public static function user_label( $id ) { $user = $id ? get_userdata( absint( $id ) ) : false; if ( $user ) { return $user->display_name; } if ( $id ) { /* translators: %d: ID of an account no longer available. */ return sprintf( __( 'User #%d (unavailable)', 'adminwatch-central' ), absint( $id ) ); } return __( 'System / WordPress', 'adminwatch-central' ); } /** Render known templates, never stored messages or HTML. */ public static function message( $row ) { $actor = self::user_label( $row->actor_user_id ); $object = 'user' === $row->object_type ? self::user_label( $row->object_id ) : $row->object_label; switch ( $row->event_type ) { case 'login_succeeded': /* translators: %s: User display name. */ return sprintf( __( '%s logged in.', 'adminwatch-central' ), $actor ); case 'logout': /* translators: %s: User display name. */ return sprintf( __( '%s logged out.', 'adminwatch-central' ), $actor ); case 'login_failed': if ( '' === $row->object_label ) { return __( 'Failed login attempts with other or unspecified identifiers.', 'adminwatch-central' ); } /* translators: %s: Bounded attempted username or email address. */ return sprintf( __( 'Failed login using identifier "%s".', 'adminwatch-central' ), $row->object_label ); case 'user_created': /* translators: 1: Actor. 2: Target account. */ return sprintf( __( '%1$s created account %2$s.', 'adminwatch-central' ), $actor, $object ); case 'user_deleted': /* translators: 1: Actor. 2: Target account. */ return sprintf( __( '%1$s deleted account %2$s.', 'adminwatch-central' ), $actor, $object ); case 'user_role_changed': /* translators: 1: Actor. 2: Target account. */ return sprintf( __( '%1$s changed roles for %2$s.', 'adminwatch-central' ), $actor, $object ); case 'password_changed': /* translators: %s: Account whose password was changed; the actor may be unknown. */ return sprintf( __( 'Password changed for %s.', 'adminwatch-central' ), $object ); case 'post_created': /* translators: 1: Actor. 2: Content title. */ return sprintf( __( '%1$s created "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'post_updated': /* translators: 1: Actor. 2: Content title. */ return sprintf( __( '%1$s updated "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'post_trashed': /* translators: 1: Actor. 2: Content title. */ return sprintf( __( '%1$s moved "%2$s" to Trash.', 'adminwatch-central' ), $actor, $object ); case 'post_restored': /* translators: 1: Actor. 2: Content title. */ return sprintf( __( '%1$s restored "%2$s" from Trash.', 'adminwatch-central' ), $actor, $object ); case 'post_deleted': /* translators: 1: Actor. 2: Content title. */ return sprintf( __( '%1$s permanently deleted "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'plugin_installed': /* translators: 1: Actor. 2: Plugin name. */ return sprintf( __( '%1$s installed plugin "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'plugin_activated': /* translators: 1: Actor. 2: Plugin name. */ return sprintf( __( '%1$s activated plugin "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'plugin_deactivated': /* translators: 1: Actor. 2: Plugin name. */ return sprintf( __( '%1$s deactivated plugin "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'plugin_updated': /* translators: 1: Actor. 2: Plugin name. */ return sprintf( __( '%1$s updated plugin "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'plugin_deleted': /* translators: 1: Actor. 2: Plugin name. */ return sprintf( __( '%1$s deleted plugin "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'theme_installed': /* translators: 1: Actor. 2: Theme name. */ return sprintf( __( '%1$s installed theme "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'theme_updated': /* translators: 1: Actor. 2: Theme name. */ return sprintf( __( '%1$s updated theme "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'theme_deleted': /* translators: 1: Actor. 2: Theme name. */ return sprintf( __( '%1$s deleted theme "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'theme_switched': /* translators: 1: Actor. 2: Theme name. */ return sprintf( __( '%1$s switched to theme "%2$s".', 'adminwatch-central' ), $actor, $object ); case 'wordpress_updated': return __( 'WordPress core update completed.', 'adminwatch-central' ); case 'settings_updated': /* translators: %s: Actor. */ return sprintf( __( '%s updated Admin Watch Central settings.', 'adminwatch-central' ), $actor ); } return __( 'Activity recorded.', 'adminwatch-central' ); } /** Human-readable details from known metadata fields only. */ public static function details( $row ) { $data = json_decode( (string) $row->event_data, true ); $data = is_array( $data ) ? $data : array(); if ( 'login_failed' === $row->event_type ) { /* translators: %d: Number of attempts within this 15-minute bucket. */ return sprintf( _n( '%d attempt', '%d attempts', $row->occurrence_count, 'adminwatch-central' ), $row->occurrence_count ); } if ( isset( $data['old_version'], $data['new_version'] ) ) { /* translators: 1: Previous version. 2: New version. */ return sprintf( __( 'Version %1$s → %2$s', 'adminwatch-central' ), $data['old_version'], $data['new_version'] ); } if ( isset( $data['old_roles'], $data['new_roles'] ) ) { $old_roles = '' === $data['old_roles'] ? __( 'No role', 'adminwatch-central' ) : $data['old_roles']; $new_roles = '' === $data['new_roles'] ? __( 'No role', 'adminwatch-central' ) : $data['new_roles']; /* translators: 1: Previous role slugs. 2: New role slugs. */ return sprintf( __( 'Roles: %1$s → %2$s', 'adminwatch-central' ), $old_roles, $new_roles ); } if ( isset( $data['old_theme'] ) ) { /* translators: %s: Previous theme name. */ return sprintf( __( 'Previous theme: %s', 'adminwatch-central' ), $data['old_theme'] ); } if ( 'settings_updated' === $row->event_type ) { $labels = array( 'monitor_404s' => __( '404 monitoring', 'adminwatch-central' ), 'retention_days' => __( '404 retention', 'adminwatch-central' ), 'delete_data' => __( 'Delete data on uninstall', 'adminwatch-central' ), 'monitor_activity' => __( 'Site Activity', 'adminwatch-central' ), 'activity_retention_days' => __( 'Activity retention', 'adminwatch-central' ), 'record_failed_logins' => __( 'Failed-login recording', 'adminwatch-central' ), ); $parts = array(); foreach ( $labels as $key => $label ) { if ( isset( $data[ $key ] ) ) { /* translators: 1: Setting label. 2: Previous and new values. */ $parts[] = sprintf( __( '%1$s: %2$s', 'adminwatch-central' ), $label, $data[ $key ] ); } } return implode( '; ', $parts ); } return ''; } }