prefix . self::TABLE; } public function register_hooks() { add_action( 'admin_init', array( $this, 'maybe_install' ), 1 ); add_action( 'admin_init', array( $this, 'log_visit' ), 20 ); add_action( self::CRON_HOOK, array( $this, 'prune' ) ); if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', self::CRON_HOOK ); } } public function maybe_install() { if ( (int) get_option( self::SCHEMA_OPTION ) >= self::SCHEMA_VERSION ) { return; } if ( ! AIEO_Schema_Lock::acquire( self::SCHEMA_OPTION ) ) { return; } global $wpdb; $table = self::table(); $collate = aieo_get_charset_collate(); $sql = "CREATE TABLE {$table} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL, page VARCHAR(64) NOT NULL DEFAULT '', tab VARCHAR(64) NOT NULL DEFAULT '', session_id INT UNSIGNED NOT NULL DEFAULT 0, viewed_at DATETIME NOT NULL, PRIMARY KEY (id), KEY user_time (user_id, viewed_at), KEY viewed (viewed_at), KEY tab (tab) ) {$collate};"; if ( function_exists( 'aieo_dbdelta' ) ) { aieo_dbdelta( $sql ); } else { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); } update_option( self::SCHEMA_OPTION, self::SCHEMA_VERSION, false ); } public function log_visit() { if ( wp_doing_ajax() ) { return; } if ( isset( $_SERVER['REQUEST_METHOD'] ) && strtoupper( (string) $_SERVER['REQUEST_METHOD'] ) !== 'GET' ) { return; } $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; if ( $page === '' || ( strpos( $page, 'ai-eshop-' ) !== 0 && strpos( $page, 'aieo-' ) !== 0 ) ) { return; } $uid = get_current_user_id(); if ( ! $uid ) { return; } $tab = isset( $_GET['dmm_tab'] ) ? sanitize_key( wp_unslash( $_GET['dmm_tab'] ) ) : ''; $now = time(); $seen = (int) get_user_meta( $uid, self::SEEN_META, true ); $sess = (int) get_user_meta( $uid, self::SESSION_META, true ); if ( ! $sess || ( $now - $seen ) > self::SESSION_GAP ) { $sess++; update_user_meta( $uid, self::SESSION_META, $sess ); } update_user_meta( $uid, self::SEEN_META, $now ); global $wpdb; $wpdb->insert( self::table(), array( 'user_id' => $uid, 'page' => $page, 'tab' => $tab, 'session_id' => $sess, 'viewed_at' => current_time( 'mysql', true ), ), array( '%d', '%s', '%s', '%d', '%s' ) ); if ( $page === AIEO_DMM::MENU_SLUG && $tab !== '' ) { update_user_meta( $uid, self::LAST_TAB_META, $tab ); } } public static function last_tab_for_user( $uid ) { return $uid ? (string) get_user_meta( (int) $uid, self::LAST_TAB_META, true ) : ''; } public function prune() { global $wpdb; $cutoff = gmdate( 'Y-m-d H:i:s', time() - self::RETENTION_DAYS * DAY_IN_SECONDS ); $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . self::table() . ' WHERE viewed_at < %s', $cutoff ) ); } private function page_labels() { return array( 'ai-eshop-optimizer' => __( 'Data Prep & Analytics', 'ai-eshop-optimizer' ), 'ai-eshop-dmm' => __( 'e-Shop Manager', 'ai-eshop-optimizer' ), 'ai-eshop-chat' => __( 'AI Chat', 'ai-eshop-optimizer' ), 'aieo-iapi' => __( 'Interactivity (IAPI)', 'ai-eshop-optimizer' ), 'aieo-recommendations-cache' => __( 'Recommendations Cache', 'ai-eshop-optimizer' ), ); } public function render_subtab() { if ( ! current_user_can( 'activate_plugins' ) ) { list( $aieo_pu_open, $aieo_pu_close ) = AIEO_DMM_UI::card( array( 'anchor' => 'plugin-usage-overview', 'icon' => 'activity', 'title' => __( 'Admin activity', 'ai-eshop-optimizer' ) ) ); echo $aieo_pu_open . '

' . esc_html__( 'This panel is available to site administrators only.', 'ai-eshop-optimizer' ) . '

' . $aieo_pu_close; return; } $this->maybe_install(); $ranges = array( 7 => __( '7 days', 'ai-eshop-optimizer' ), 30 => __( '30 days', 'ai-eshop-optimizer' ), 90 => __( '90 days', 'ai-eshop-optimizer' ), 365 => __( '1 year', 'ai-eshop-optimizer' ), 0 => __( 'All time', 'ai-eshop-optimizer' ) ); $days = isset( $_GET['audit_days'] ) ? max( 0, min( 3650, (int) $_GET['audit_days'] ) ) : 30; if ( ! isset( $ranges[ $days ] ) ) { $ranges[ $days ] = sprintf( __( '%d days', 'ai-eshop-optimizer' ), $days ); } $r = $this->report( $days ); $mod_labels = AIEO_DMM::instance()->get_modules(); $page_lbl = $this->page_labels(); $base_url = AIEO_DMM::instance()->get_subtab_url( $this->get_id() ); $card_label = function ( $tab ) use ( $mod_labels ) { return ( $tab !== '' && isset( $mod_labels[ $tab ] ) ) ? $mod_labels[ $tab ]->get_label() : ( $tab !== '' ? $tab : '—' ); }; $ago = function ( $gmt ) { if ( empty( $gmt ) ) { return '—'; } $ts = strtotime( $gmt . ' UTC' ); return $ts ? sprintf( __( '%s ago', 'ai-eshop-optimizer' ), human_time_diff( $ts ) ) : '—'; }; ?> 1 ) ); list( $aieo_pu_open, $aieo_pu_close ) = AIEO_DMM_UI::card( array( 'anchor' => 'plugin-usage-overview', 'icon' => 'activity', 'title' => __( 'Admin activity', 'ai-eshop-optimizer' ), 'description' => __( 'Who is using the plugin, and which panels. Admin page views only — no customer data. Visible to site administrators.', 'ai-eshop-optimizer' ), 'footnote' => sprintf( __( 'Data is kept for %d days, then pruned automatically.', 'ai-eshop-optimizer' ), self::RETENTION_DAYS ), ) ); echo $aieo_pu_open; ?>
$lbl ) { echo \Rooster\Admin\Ui::button( $lbl, array( 'size' => 'sm', 'variant' => ( (int) $d === (int) $days ) ? 'primary' : 'outline', 'href' => add_query_arg( 'audit_days', $d, $base_url ) ) ); } ?>
__( 'No activity in this window yet', 'ai-eshop-optimizer' ), 'text' => __( 'Usage is logged from now on.', 'ai-eshop-optimizer' ), 'compact' => true, ) ); } ?> (int) $row['user_id'], 'name' => $u ? $u->display_name : sprintf( __( 'User #%d (removed)', 'ai-eshop-optimizer' ), (int) $row['user_id'] ), 'role' => $u && ! empty( $u->roles ) ? implode( ', ', $u->roles ) : '', 'visits' => (int) $row['visits'], 'sessions' => (int) $row['sessions'], 'last' => $ago( $row['last_seen'] ), 'last_raw' => (string) $row['last_seen'], 'top' => isset( $r['user_top'][ $row['user_id'] ] ) ? $card_label( $r['user_top'][ $row['user_id'] ] ) : '', ); } AIEO_Table::card( array( 'anchor' => 'plugin-usage-by-user', 'icon' => 'users', 'heading' => 'h2', 'title' => __( 'By user', 'ai-eshop-optimizer' ), 'description' => __( 'See who on your team opens the plugin\'s screens, how often, when they were last active and which card each uses most.', 'ai-eshop-optimizer' ), 'page_id' => 'plugin_usage', 'row_key' => 'user_id', 'rows' => $aieo_pu_users, 'columns' => array( array( 'key' => 'name', 'label' => __( 'User', 'ai-eshop-optimizer' ) ), array( 'key' => 'role', 'label' => __( 'Role', 'ai-eshop-optimizer' ) ), array( 'key' => 'visits', 'label' => __( 'Views', 'ai-eshop-optimizer' ), 'type' => 'integer' ), array( 'key' => 'sessions', 'label' => __( 'Sessions', 'ai-eshop-optimizer' ), 'type' => 'integer' ), array( 'key' => 'last', 'label' => __( 'Last active', 'ai-eshop-optimizer' ), 'sort_key' => 'last_raw', 'nowrap' => true ), array( 'key' => 'top', 'label' => __( 'Most-used card', 'ai-eshop-optimizer' ) ), ), ) ); ?> 'plugin-usage-most-used-cards', 'icon' => 'layout-grid', 'heading' => 'h2', 'title' => __( 'Most-used cards', 'ai-eshop-optimizer' ), 'description' => __( 'See which of the plugin\'s cards your team opens most often in the chosen period, and how many people open each.', 'ai-eshop-optimizer' ), 'page_id' => 'plugin_usage', 'row_key' => 'tab', 'rows' => array_map( static function ( $c ) use ( $card_label ) { return array( 'tab' => (string) $c['tab'], 'card' => $card_label( $c['tab'] ), 'visits' => (int) $c['visits'], 'users' => (int) $c['users'], ); }, (array) $r['top_cards'] ), 'columns' => array( array( 'key' => 'card', 'label' => __( 'Card', 'ai-eshop-optimizer' ) ), array( 'key' => 'visits', 'label' => __( 'Views', 'ai-eshop-optimizer' ), 'type' => 'integer' ), array( 'key' => 'users', 'label' => __( 'Users', 'ai-eshop-optimizer' ), 'type' => 'integer' ), ), ) ); ?> 'plugin-usage-by-page', 'icon' => 'file-text', 'heading' => 'h2', 'title' => __( 'By page', 'ai-eshop-optimizer' ), 'description' => __( 'See how many times each admin page of the plugin was viewed in the chosen period.', 'ai-eshop-optimizer' ), 'page_id' => 'plugin_usage', 'row_key' => 'page', 'rows' => array_map( static function ( $p ) use ( $page_lbl ) { return array( 'page' => (string) $p['page'], 'label' => isset( $page_lbl[ $p['page'] ] ) ? $page_lbl[ $p['page'] ] : $p['page'], 'visits' => (int) $p['visits'], ); }, (array) $r['by_page'] ), 'columns' => array( array( 'key' => 'label', 'label' => __( 'Page', 'ai-eshop-optimizer' ) ), array( 'key' => 'visits', 'label' => __( 'Views', 'ai-eshop-optimizer' ), 'type' => 'integer' ), ), ) ); ?> 0 ) { $where = 'viewed_at >= %s'; $args[] = gmdate( 'Y-m-d H:i:s', time() - $days * DAY_IN_SECONDS ); } $totals = $wpdb->get_row( $args ? $wpdb->prepare( "SELECT COUNT(*) visits, COUNT(DISTINCT user_id) users, COUNT(DISTINCT CONCAT(user_id,'-',session_id)) sessions, COUNT(DISTINCT CASE WHEN tab<>'' THEN tab END) cards FROM {$t} WHERE {$where}", $args ) : "SELECT COUNT(*) visits, COUNT(DISTINCT user_id) users, COUNT(DISTINCT CONCAT(user_id,'-',session_id)) sessions, COUNT(DISTINCT CASE WHEN tab<>'' THEN tab END) cards FROM {$t}", ARRAY_A ); $per_user = $wpdb->get_results( $args ? $wpdb->prepare( "SELECT user_id, COUNT(*) visits, COUNT(DISTINCT session_id) sessions, MAX(viewed_at) last_seen FROM {$t} WHERE {$where} GROUP BY user_id ORDER BY visits DESC", $args ) : "SELECT user_id, COUNT(*) visits, COUNT(DISTINCT session_id) sessions, MAX(viewed_at) last_seen FROM {$t} GROUP BY user_id ORDER BY visits DESC", ARRAY_A ); $top_cards = $wpdb->get_results( $args ? $wpdb->prepare( "SELECT tab, COUNT(*) visits, COUNT(DISTINCT user_id) users FROM {$t} WHERE {$where} AND tab<>'' GROUP BY tab ORDER BY visits DESC LIMIT 20", $args ) : "SELECT tab, COUNT(*) visits, COUNT(DISTINCT user_id) users FROM {$t} WHERE tab<>'' GROUP BY tab ORDER BY visits DESC LIMIT 20", ARRAY_A ); $by_page = $wpdb->get_results( $args ? $wpdb->prepare( "SELECT page, COUNT(*) visits FROM {$t} WHERE {$where} GROUP BY page ORDER BY visits DESC", $args ) : "SELECT page, COUNT(*) visits FROM {$t} GROUP BY page ORDER BY visits DESC", ARRAY_A ); $rows = $wpdb->get_results( $args ? $wpdb->prepare( "SELECT user_id, tab, COUNT(*) c FROM {$t} WHERE {$where} AND tab<>'' GROUP BY user_id, tab", $args ) : "SELECT user_id, tab, COUNT(*) c FROM {$t} WHERE tab<>'' GROUP BY user_id, tab", ARRAY_A ); $user_top = array(); $best = array(); foreach ( (array) $rows as $row ) { $uid = (int) $row['user_id']; if ( ! isset( $best[ $uid ] ) || (int) $row['c'] > $best[ $uid ] ) { $best[ $uid ] = (int) $row['c']; $user_top[ $uid ] = $row['tab']; } } return array( 'totals' => $totals ? $totals : array( 'visits' => 0, 'users' => 0, 'sessions' => 0, 'cards' => 0 ), 'per_user' => (array) $per_user, 'top_cards' => (array) $top_cards, 'by_page' => (array) $by_page, 'user_top' => $user_top, ); } }