array_fill_keys( array( 'class', 'viewbox', 'width', 'height', 'fill', 'aria-hidden', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin' ), true ), 'path' => array_fill_keys( array( 'd', 'fill', 'opacity' ), true ), 'rect' => array_fill_keys( array( 'x', 'y', 'width', 'height', 'rx' ), true ), 'circle' => array_fill_keys( array( 'cx', 'cy', 'r' ), true ), 'span' => array( 'class' => true, 'aria-hidden' => true ), 'strong' => array(), 'small' => array(), 'img' => array_fill_keys( array( 'src', 'alt', 'width', 'height', 'loading', 'decoding', 'referrerpolicy', 'data-lai-avatar' ), true ), ); } /** A fixed, scalable Logged.ai monogram. */ public static function mark(): string { return ''; } public static function brand(): string { return 'Logged.ai'; } public static function screen(): bool { global $plugin_page, $pagenow; return 'admin.php' === $pagenow && is_string( $plugin_page ) && ( 'logged-ai' === $plugin_page || str_starts_with( $plugin_page, 'logged-ai-' ) ); } public static function boot(): void { add_filter( 'admin_body_class', static fn( string $classes ): string => $classes . ( self::screen() ? ' lai-app' : '' ) ); add_action( 'admin_enqueue_scripts', static function (): void { if ( self::screen() ) { wp_enqueue_style( 'logged-ai-studio', plugins_url( 'assets/studio.css', LOGGED_AI_FILE ), array(), self::VERSION ); wp_enqueue_script( 'logged-ai-studio', plugins_url( 'assets/studio.js', LOGGED_AI_FILE ), array( 'wp-i18n' ), self::VERSION, true ); wp_set_script_translations( 'logged-ai-studio', 'logged-ai', plugin_dir_path( LOGGED_AI_FILE ) . 'languages' ); wp_enqueue_style( 'logged-ai-activity', plugins_url( 'assets/activity.css', LOGGED_AI_FILE ), array( 'logged-ai-studio' ), self::VERSION ); wp_enqueue_script( 'logged-ai-activity', plugins_url( 'assets/activity.js', LOGGED_AI_FILE ), array( 'logged-ai-studio', 'wp-i18n' ), self::VERSION, true ); wp_set_script_translations( 'logged-ai-activity', 'logged-ai', plugin_dir_path( LOGGED_AI_FILE ) . 'languages' ); wp_enqueue_style( 'logged-ai-settings-ui', plugins_url( 'assets/settings.css', LOGGED_AI_FILE ), array( 'logged-ai-studio' ), self::VERSION ); wp_enqueue_script( 'logged-ai-settings-ui', plugins_url( 'assets/settings.js', LOGGED_AI_FILE ), array( 'logged-ai-studio', 'wp-i18n' ), self::VERSION, true ); wp_set_script_translations( 'logged-ai-settings-ui', 'logged-ai', plugin_dir_path( LOGGED_AI_FILE ) . 'languages' ); wp_enqueue_style( 'logged-ai-value', plugins_url( 'assets/value.css', LOGGED_AI_FILE ), array( 'logged-ai-studio' ), self::VERSION ); wp_enqueue_style( 'logged-ai-sidebar', plugins_url( 'assets/sidebar.css', LOGGED_AI_FILE ), array( 'logged-ai-value' ), self::VERSION ); wp_enqueue_style( 'logged-ai-discovery-navigation', plugins_url( 'assets/discovery-navigation.css', LOGGED_AI_FILE ), array( 'logged-ai-sidebar' ), self::VERSION ); wp_enqueue_script( 'logged-ai-value', plugins_url( 'assets/value.js', LOGGED_AI_FILE ), array( 'logged-ai-studio', 'wp-i18n' ), self::VERSION, true ); wp_set_script_translations( 'logged-ai-value', 'logged-ai', plugin_dir_path( LOGGED_AI_FILE ) . 'languages' ); if ( 'logged-ai-setup' === ( $GLOBALS['plugin_page'] ?? '' ) ) { wp_enqueue_style( 'logged-ai-setup', plugins_url( 'assets/setup.css', LOGGED_AI_FILE ), array( 'logged-ai-studio' ), self::VERSION ); wp_enqueue_script( 'logged-ai-setup', plugins_url( 'assets/setup.js', LOGGED_AI_FILE ), array( 'logged-ai-studio', 'wp-i18n' ), self::VERSION, true ); wp_set_script_translations( 'logged-ai-setup', 'logged-ai', plugin_dir_path( LOGGED_AI_FILE ) . 'languages' ); } } }, 999 ); } /** Fixed decorative vector paths; never accepts arbitrary markup. */ public static function icon( string $name ): string { $paths = array( 'plans' => '', 'setup' => '', 'overview' => '', 'activity' => '', 'requests' => '', 'incidents' => '', 'response' => '', 'alerts' => '', 'reports' => '', 'settings' => '', 'arrow' => '', 'moon' => '', 'lock' => '', ); return ''; } public static function label( string $code ): string { $labels = array( 'content.post_created' => __( 'Content created', 'logged-ai' ), 'content.post_deleted' => __( 'Content deleted', 'logged-ai' ), 'content.post_trashed' => __( 'Content moved to trash', 'logged-ai' ), 'identity.user_created' => __( 'User account created', 'logged-ai' ), 'identity.user_deleted' => __( 'User account deleted', 'logged-ai' ), 'configuration.plugin_operation_failed' => __( 'Plugin operation failed', 'logged-ai' ), 'identity.roles_changed' => __( 'User permissions changed', 'logged-ai' ), 'identity.password_changed' => __( 'Password changed', 'logged-ai' ), 'identity.application_password_created' => __( 'Application password created', 'logged-ai' ), 'identity.login' => __( 'User signed in', 'logged-ai' ), 'identity.logout' => __( 'User signed out', 'logged-ai' ), 'identity.account_auth_failed_burst' => __( 'Repeated sign-in failures', 'logged-ai' ), 'identity.authentication_failed_burst' => __( 'Sign-in failure burst', 'logged-ai' ), 'identity.public_registration_completed' => __( 'Public registration completed', 'logged-ai' ), 'configuration.option_changed' => __( 'Site configuration changed', 'logged-ai' ), 'configuration.plugin_activated' => __( 'Plugin activated', 'logged-ai' ), 'configuration.plugin_updated' => __( 'Plugin updated', 'logged-ai' ), 'configuration.plugin_deactivated' => __( 'Plugin deactivated', 'logged-ai' ), 'configuration.theme_changed' => __( 'Theme changed', 'logged-ai' ), 'content.post_updated' => __( 'Content updated', 'logged-ai' ), 'audit.export_cancelled' => __( 'Export cancelled', 'logged-ai' ), 'audit.export_completed' => __( 'Export completed', 'logged-ai' ), 'audit.export_downloaded' => __( 'Export downloaded', 'logged-ai' ), 'audit.export_requested' => __( 'Export requested', 'logged-ai' ), 'audit.network_export_cancelled' => __( 'Network export cancelled', 'logged-ai' ), 'audit.network_export_completed' => __( 'Network export completed', 'logged-ai' ), 'audit.network_export_downloaded' => __( 'Network export downloaded', 'logged-ai' ), 'audit.network_export_requested' => __( 'Network export requested', 'logged-ai' ), 'commerce.coupon_changed' => __( 'Coupon changed', 'logged-ai' ), 'commerce.coupon_created' => __( 'Coupon created', 'logged-ai' ), 'commerce.order_changed' => __( 'Order changed', 'logged-ai' ), 'commerce.order_created' => __( 'Order created', 'logged-ai' ), 'commerce.order_deleted' => __( 'Order deleted', 'logged-ai' ), 'commerce.payment_setting_changed' => __( 'Payment setting changed', 'logged-ai' ), 'commerce.product_changed' => __( 'Product changed', 'logged-ai' ), 'commerce.product_created' => __( 'Product created', 'logged-ai' ), 'commerce.refund_changed' => __( 'Refund changed', 'logged-ai' ), 'commerce.refund_deleted' => __( 'Refund deleted', 'logged-ai' ), 'commerce.refund_stored' => __( 'Refund stored', 'logged-ai' ), 'commerce.stock_changed' => __( 'Stock changed', 'logged-ai' ), 'commerce.store_setting_changed' => __( 'Store setting changed', 'logged-ai' ), 'configuration.alerts_changed' => __( 'Alerts changed', 'logged-ai' ), 'configuration.automatic_suppressed' => __( 'Automatic suppressed', 'logged-ai' ), 'configuration.collection_changed' => __( 'Collection changed', 'logged-ai' ), 'configuration.core_operation_failed' => __( 'Core operation failed', 'logged-ai' ), 'configuration.core_updated' => __( 'Core updated', 'logged-ai' ), 'configuration.diagnostic_export_started' => __( 'Diagnostic export started', 'logged-ai' ), 'configuration.navigation_locations_changed' => __( 'Navigation locations changed', 'logged-ai' ), 'configuration.network_alerts_changed' => __( 'Network alerts changed', 'logged-ai' ), 'configuration.network_plugin_activated' => __( 'Network plugin activated', 'logged-ai' ), 'configuration.network_plugin_deactivated' => __( 'Network plugin deactivated', 'logged-ai' ), 'configuration.network_privacy_changed' => __( 'Network privacy changed', 'logged-ai' ), 'configuration.observer_test_started' => __( 'Observer test started', 'logged-ai' ), 'configuration.plugin_deleted' => __( 'Plugin deleted', 'logged-ai' ), 'configuration.plugin_installed' => __( 'Plugin installed', 'logged-ai' ), 'configuration.privacy_changed' => __( 'Privacy changed', 'logged-ai' ), 'configuration.request_source_changed' => __( 'Request source changed', 'logged-ai' ), 'configuration.role_capability_changed' => __( 'Role capability changed', 'logged-ai' ), 'configuration.role_definition_added' => __( 'Role definition added', 'logged-ai' ), 'configuration.role_definition_removed' => __( 'Role definition removed', 'logged-ai' ), 'configuration.sidebars_changed' => __( 'Sidebars changed', 'logged-ai' ), 'configuration.theme_deleted' => __( 'Theme deleted', 'logged-ai' ), 'configuration.theme_installed' => __( 'Theme installed', 'logged-ai' ), 'configuration.theme_operation_failed' => __( 'Theme operation failed', 'logged-ai' ), 'configuration.theme_updated' => __( 'Theme updated', 'logged-ai' ), 'configuration.uninstall_choice_changed' => __( 'Uninstall choice changed', 'logged-ai' ), 'configuration.widget_changed' => __( 'Widget changed', 'logged-ai' ), 'content.comment_created' => __( 'Comment created', 'logged-ai' ), 'content.comment_deleted' => __( 'Comment deleted', 'logged-ai' ), 'content.comment_status_changed' => __( 'Comment status changed', 'logged-ai' ), 'content.comment_updated' => __( 'Comment updated', 'logged-ai' ), 'content.navigation_item_configuration_changed' => __( 'Navigation item configuration changed', 'logged-ai' ), 'content.post_changed' => __( 'Post changed', 'logged-ai' ), 'content.term_created' => __( 'Term created', 'logged-ai' ), 'content.term_deleted' => __( 'Term deleted', 'logged-ai' ), 'content.term_updated' => __( 'Term updated', 'logged-ai' ), 'content.terms_changed' => __( 'Terms changed', 'logged-ai' ), 'identity.application_password_deleted' => __( 'Application password deleted', 'logged-ai' ), 'identity.capability_changed' => __( 'Capability changed', 'logged-ai' ), 'identity.password_rehashed' => __( 'Password rehashed', 'logged-ai' ), 'identity.password_reset_completed' => __( 'Password reset completed', 'logged-ai' ), 'identity.password_set' => __( 'Password set', 'logged-ai' ), 'identity.profile_changed' => __( 'Profile changed', 'logged-ai' ), 'identity.reset_mail_result' => __( 'Reset mail result', 'logged-ai' ), 'identity.reset_request_accepted' => __( 'Reset request accepted', 'logged-ai' ), 'identity.reset_requested' => __( 'Reset requested', 'logged-ai' ), 'identity.site_membership_added' => __( 'Site membership added', 'logged-ai' ), 'identity.site_membership_removed' => __( 'Site membership removed', 'logged-ai' ), 'identity.super_admin_granted' => __( 'Super admin granted', 'logged-ai' ), 'identity.super_admin_revoked' => __( 'Super admin revoked', 'logged-ai' ), 'network.option_changed' => __( 'Option changed', 'logged-ai' ), 'network.site_archived' => __( 'Site archived', 'logged-ai' ), 'network.site_changed' => __( 'Site changed', 'logged-ai' ), 'network.site_created' => __( 'Site created', 'logged-ai' ), 'network.site_deleted' => __( 'Site deleted', 'logged-ai' ), 'network.site_restored' => __( 'Site restored', 'logged-ai' ), 'privacy.identity_erased' => __( 'Identity erased', 'logged-ai' ), 'privacy.network_identity_erased' => __( 'Network identity erased', 'logged-ai' ), 'privacy.network_retention_pruned' => __( 'Network retention pruned', 'logged-ai' ), 'privacy.retention_pruned' => __( 'Retention pruned', 'logged-ai' ), ); return $labels[ $code ] ?? ucfirst( str_replace( array( '.', '_' ), ' ', preg_replace( '/^[^.]+\./', '', $code ) ) ); } public static function experience(): void { echo ( '
' . esc_html__( 'Display detail', 'logged-ai' ) . '' . esc_html__( 'Readable stories and guided controls.', 'logged-ai' ) . '' . esc_html__( 'Changes what you see, not collection, alerts, response or permissions.', 'logged-ai' ) . '
' ); } public static function time_label( TimeDisplay $time, mixed $value ): string { $text = $time->text( $value ); $date = \DateTimeImmutable::createFromFormat( '!Y-m-d H:i:s', substr( $text, 0, 19 ) ); return false === $date ? $text : wp_date( _x( 'M j · H:i', 'compact activity date format', 'logged-ai' ), $date->getTimestamp(), $date->getTimezone() ); } private static function guide( string $group ): void { $help = array( 'activity' => array( __( 'Start with a change you recognise.', 'logged-ai' ), __( 'Open an event to see the actor, the affected object and the captured result. Use filters when you know what you are looking for.', 'logged-ai' ) ), 'requests' => array( __( 'A finding is a reason to look closer.', 'logged-ai' ), __( 'Review the observed pattern and source context before choosing a response. Collection can miss cache hits and upstream traffic.', 'logged-ai' ) ), 'incidents' => array( __( 'Follow the evidence, one event at a time.', 'logged-ai' ), __( 'Open a case, review its retained members and record a decision. A connection between events does not establish why they happened.', 'logged-ai' ) ), 'response' => array( __( 'Preview first. Decide second.', 'logged-ai' ), __( 'Check what a policy matches, its expiry and the recovery controls before confirming it. Automatic response begins switched off.', 'logged-ai' ) ), 'alerts' => array( __( 'Only ask for the notifications you need.', 'logged-ai' ), __( 'Choose useful events and check the destination. Delivery acceptance does not guarantee someone received or read the message.', 'logged-ai' ) ), 'reports' => array( __( 'Start with a useful view.', 'logged-ai' ), __( 'Filter your evidence before exporting it. Pro adds saved searches, local summaries and scheduled reviews.', 'logged-ai' ) ), 'settings' => array( __( 'Make the workspace fit your site.', 'logged-ai' ), __( 'Review collection, privacy and retention first. Display detail changes presentation only; collection, alerts, response and permissions stay the same.', 'logged-ai' ) ), ); if ( ! isset( $help[ $group ] ) ) { return; } echo '
' . wp_kses( self::icon( $group ), Design::allowed_markup() ) . '

' . esc_html( $help[ $group ][0] ) . '

' . esc_html( $help[ $group ][1] ) . '

'; } public static function upgrade_card(): void { echo ( '
' . esc_html__( 'LOGGED.AI / PRO', 'logged-ai' ) . '

' . esc_html__( 'Less repeated work.', 'logged-ai' ) . '
' . esc_html__( 'Clearer handovers.', 'logged-ai' ) . '

' . esc_html__( 'Schedule private reports, configure team alerts and keep investigation notes with the evidence.', 'logged-ai' ) . '

' . esc_html__( 'For regular reviews, teams and client work.', 'logged-ai' ) . '
' ); } public static function upgrade_dialog( bool $pro = false, ?string $workspace_url = null, array $destinations = array() ): void { if ( $pro && ! $destinations && $workspace_url ) { $destinations['investigate'] = array('url'=>$workspace_url,'label'=>__( 'Open Pro workspace', 'logged-ai' )); } DesignPro::render( $pro, $destinations, true ); } public static function preference_scope(): string { return substr( hash( 'sha256', admin_url() . '|' . get_current_blog_id() . '|' . get_current_user_id() ), 0, 24 ); } /** * Render registered destinations without altering route authorization. * * @param array $groups Permission-filtered navigation groups. * @param string $current Current group key. * @param string $base Current site or network admin URL. */ private static function navigation_links( array $groups, string $current, string $base ): void { foreach ( $groups as $key => $group ) { $slug = array_key_first( $group['pages'] ); $label = 'settings' === $key ? __( 'Settings', 'logged-ai' ) : $group['label']; $icon = 'plugins' === $key ? 'overview' : $key; echo '' . wp_kses( self::icon( $icon ), self::allowed_markup() ) . '' . esc_html( $label ) . '' . ( $key === $current ? '' : '' ) . ''; } } /** $groups contains only registered routes allowed by Navigation::available(). */ public static function shell( array $groups, bool $network, string $page ): void { $current = null; $pro = defined( 'LOGGED_AI_PRO_FILE' ); foreach ( $groups as $key => $group ) { if ( isset( $group['pages'][ $page ] ) ) { $current = $key; } if ( isset( $group['pages']['logged-ai-investigations'] ) || isset( $group['pages']['logged-ai-network-priority'] ) ) { $pro = true; } } if ( null === $current ) { return; } $base = $network ? Scope::network_admin_url( 'admin.php' ) : admin_url( 'admin.php' ); echo '
' . esc_html__( 'Workspace', 'logged-ai' ) . ' ' . '/ ' ) . esc_html( $groups[ $current ]['label'] ) . '
'; if ( $pro ) { echo '' . esc_html__( 'PRO WORKSPACE', 'logged-ai' ) . ''; } echo '' . wp_kses( self::icon( 'lock' ), Design::allowed_markup() ) . ( ' ' . esc_html__( 'Local storage', 'logged-ai' ) . '
'; if ( count( $groups[ $current ]['pages'] ) > 1 ) { echo ( ''; } if ( ! $network ) { DesignPro::continuation(); } if ( ! in_array( $page, array( 'logged-ai-overview', 'logged-ai-setup', 'logged-ai-plans', 'logged-ai-commercial', 'logged-ai-commercial-updates', 'logged-ai-more-plugins' ), true ) ) { self::experience(); } if ( 'logged-ai-plans' !== $page ) { echo '' ); self::upgrade_dialog( $pro, null, DesignPro::destinations( $groups, $base ) ); } } }