*/ const MENU_ICON = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5NiA5NiI+PHBhdGggZmlsbD0iYmxhY2siIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTI3IDEySDY5QzgyIDEyIDkyIDIyIDkyIDM1VjU1QzkyIDY4IDgyIDc4IDY5IDc4SDQwTDI0IDkxQzIyLjMgOTIuNCAyMCA5MS4yIDIwIDg5Vjc3QzEwLjUgNzQgNCA2NSA0IDU1VjM1QzQgMjIgMTQgMTIgMjcgMTJaTTI5IDMySDY3QTQgNCAwIDAgMSA2NyA0MEgyOUE0IDQgMCAwIDEgMjkgMzJaTTI5IDQ5SDU1QTQgNCAwIDAgMSA1NSA1N0gyOUE0IDQgMCAwIDEgMjkgNDlaIi8+PC9zdmc+'; /** * Register hooks. * * @return void */ public function register() { add_action( 'admin_menu', array( $this, 'add_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } /** * Tabs: slug => label. * * @return array */ public static function tabs() { return array( 'overview' => __( 'Overview', '85pl-android-sms-api' ), 'rules' => __( 'Rules', '85pl-android-sms-api' ), 'sent' => __( 'Sent', '85pl-android-sms-api' ), 'replies' => __( 'Replies', '85pl-android-sms-api' ), 'log' => __( 'Log', '85pl-android-sms-api' ), 'blocked' => __( 'Blocked', '85pl-android-sms-api' ), 'settings' => __( 'Settings', '85pl-android-sms-api' ), ); } /** * URL of a tab. Unknown tabs fall back to the overview. * * @param string $tab Tab slug. * @return string */ public static function tab_url( $tab ) { if ( ! array_key_exists( $tab, self::tabs() ) ) { $tab = 'overview'; } return add_query_arg( array( 'page' => self::SLUG, 'tab' => $tab, ), admin_url( 'admin.php' ) ); } /** * Add the top-level menu. * * @return void */ public function add_menu() { add_menu_page( __( '85.pl | Android SMS API', '85pl-android-sms-api' ), __( '85.pl | Android SMS API', '85pl-android-sms-api' ), self::CAPABILITY, self::SLUG, array( $this, 'render_page' ), self::MENU_ICON, 58 ); } /** * Load admin CSS on the plugin screen only. * * @param string $hook_suffix Current admin page hook. * @return void */ public function enqueue_assets( $hook_suffix ) { if ( 'toplevel_page_' . self::SLUG !== $hook_suffix ) { return; } wp_enqueue_style( 'smsportal-admin', SMSPORTAL_URL_BASE . 'assets/admin.css', array(), SMSPORTAL_VERSION ); } /** * Currently selected tab. * * @return string */ private function current_tab() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab switch. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'overview'; return array_key_exists( $tab, self::tabs() ) ? $tab : 'overview'; } /** * Render the plugin screen. * * @return void */ public function render_page() { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', '85pl-android-sms-api' ), '', array( 'response' => 403 ) ); } $tabs = self::tabs(); $current_tab = $this->current_tab(); $logo_url = SMSPORTAL_URL_BASE . 'assets/logo.svg'; if ( 'overview' === $current_tab ) { // Fetch the phone state before the header, so its pill shows what the overview shows. OverviewPage::prime(); } $smsportal_pill = self::connection_pill(); echo '
'; include SMSPORTAL_DIR . 'includes/Admin/views/header.php'; Notice::render(); StatusNotices::render(); echo '
'; $this->render_tab( $current_tab ); echo '
'; } /** * Connection pill in the header. Uses only the cached phone state: the header never calls smsportal.app. * * @return array{tone:string,text:string} tone: ok, wait or off. */ public static function connection_pill(): array { $connection = Connection::get(); if ( null === $connection ) { return array( 'tone' => 'off', 'text' => __( 'Not connected', '85pl-android-sms-api' ), ); } if ( Connection::home_changed() ) { return array( 'tone' => 'wait', 'text' => __( 'Connection paused', '85pl-android-sms-api' ), ); } $info = Api::cached_info(); $name = (string) ( $info['device_name'] ?? $connection['device_name'] ?? '' ); $name = '' === $name ? __( 'Phone', '85pl-android-sms-api' ) : $name; if ( null === $info ) { return array( 'tone' => 'ok', /* translators: %s: name of the phone, e.g. "Pixel 6". */ 'text' => sprintf( __( 'Connected · %s', '85pl-android-sms-api' ), $name ), ); } $online = ! empty( $info['device_online'] ); return array( 'tone' => $online ? 'ok' : 'wait', 'text' => $online /* translators: %s: name of the phone, e.g. "Pixel 6". */ ? sprintf( __( 'Connected · %s online', '85pl-android-sms-api' ), $name ) /* translators: %s: name of the phone, e.g. "Pixel 6". */ : sprintf( __( 'Connected · %s offline', '85pl-android-sms-api' ), $name ), ); } /** * Body of a tab. * * @param string $tab Tab slug. * @return void */ private function render_tab( string $tab ) { if ( 'overview' === $tab ) { OverviewPage::render(); } elseif ( 'rules' === $tab ) { RulesPage::render(); } elseif ( 'settings' === $tab ) { SettingsPage::render(); } else { ListPages::render( $tab ); } } }