array( 'label' => 'Home', 'icon' => 'admin-home', 'desc' => 'How your assistant is doing, and what needs you', 'panes' => array( 'overview' => array( 'label' => 'Overview', 'render' => 'cwp_ai_render_home', 'order' => 10, ), 'how' => array( 'label' => 'How it works', 'render' => 'cwp_ai_render_how_it_works', 'order' => 20, ), ), ), 'knowledge' => array( 'label' => 'Knowledge', 'icon' => 'welcome-learn-more', 'desc' => 'What your assistant knows and how it answers', 'panes' => array( 'entries' => array( 'label' => 'Entries', 'render' => function () { cwp_ai_render_knowledge_pane( array( 'all' ) ); }, 'order' => 10, ), 'add' => array( 'label' => 'Add and import', 'render' => function () { cwp_ai_render_knowledge_pane( (array) apply_filters( 'cwp_ai_knowledge_add_panels', array( 'qa', 'csv-import' ) ) ); }, 'order' => 20, ), 'test' => array( 'label' => 'Test answers', 'render' => function () { $ctx = cwp_ai_admin_context(); cwp_ai_render_vector_tab( $ctx['qa_pairs'] ); }, 'order' => 40, ), ), ), 'conversations' => array( 'label' => 'Conversations', 'icon' => 'format-chat', 'desc' => 'Enquiries from your visitors', 'panes' => array( 'enquiries' => array( 'label' => 'Enquiries', 'render' => 'cwp_ai_render_leads_entries', 'order' => 10, ), ), ), 'design' => array( 'label' => 'Design', 'icon' => 'art', 'desc' => 'How the chat looks and what it offers', 'panes' => array( 'look' => array( 'label' => 'Look and feel', 'render' => 'cwp_ai_render_appearance_tab', 'order' => 10, ), 'form' => array( 'label' => 'Contact form', 'render' => 'cwp_ai_render_form_setup', 'order' => 20, ), ), ), 'analytics' => array( 'label' => 'Analytics', 'icon' => 'chart-area', 'desc' => 'What visitors ask and do', 'panes' => array( 'overview' => array( 'label' => 'Overview', 'render' => 'cwp_ai_render_analytics_tab', 'order' => 10, ), 'speed' => array( 'label' => 'Site speed', 'render' => 'cwp_ai_render_performance_tab', 'order' => 90, ), ), ), 'settings' => array( 'label' => 'Settings', 'icon' => 'admin-settings', 'desc' => 'Where it shows, who sees it and your account', 'panes' => array( 'general' => array( 'label' => 'General', 'render' => 'cwp_ai_render_settings_general', 'order' => 10, ), 'account' => array( 'label' => 'Account', 'render' => 'cwp_ai_render_settings_account', 'order' => 90, ), ), ), ) ); foreach ( $sections as $key => $section ) { $panes = (array) ( $section['panes'] ?? array() ); uasort( $panes, function ( $a, $b ) { return (int) ( $a['order'] ?? 50 ) <=> (int) ( $b['order'] ?? 50 ); } ); $sections[ $key ]['panes'] = $panes; } return $sections; } /** * Maps an old screen name, or a section name, to its section and pane. * * @param string $name Screen name. * @return array{0:string,1:string} */ function cwp_ai_route( $name ) { $map = (array) apply_filters( 'cwp_ai_route_aliases', array( 'setup' => array( 'setup', '' ), 'home' => array( 'home', '' ), 'knowledge' => array( 'knowledge', '' ), 'vector' => array( 'knowledge', 'test' ), 'leads' => array( 'conversations', 'enquiries' ), 'enquiries' => array( 'conversations', 'enquiries' ), 'appearance' => array( 'design', 'look' ), 'form' => array( 'design', 'form' ), 'analytics' => array( 'analytics', '' ), 'performance' => array( 'analytics', 'speed' ), 'settings' => array( 'settings', '' ), 'usage' => array( 'settings', 'account' ), 'transfer' => array( 'settings', 'account' ), ) ); if ( false !== strpos( $name, '/' ) ) { list( $section, $pane ) = explode( '/', $name, 2 ); return isset( cwp_ai_sections()[ $section ] ) ? array( $section, sanitize_key( $pane ) ) : array( 'home', '' ); } if ( isset( $map[ $name ] ) ) { return $map[ $name ]; } return isset( cwp_ai_sections()[ $name ] ) ? array( $name, '' ) : array( 'home', '' ); } /** * Returns the admin page slug for a section. * * @param string $section Section name. * @return string */ function cwp_ai_admin_slug( $section ) { return 'home' === $section ? 'cloudy-ai-chatbot' : 'cloudy-ai-chatbot-' . $section; } /** * Returns the admin address of a screen, accepting old screen names too. * * @param string $name Section, pane shortcut or old screen name. * @param string $pane Pane inside the section. * @return string */ function cwp_ai_admin_url( $name, $pane = '' ) { list( $section, $mapped ) = cwp_ai_route( $name ); $pane = '' !== $pane ? $pane : $mapped; $url = admin_url( 'admin.php?page=' . cwp_ai_admin_slug( $section ) ); return '' !== $pane ? add_query_arg( 'pane', $pane, $url ) : $url; } /** * Tells whether an admin page hook belongs to this plugin. * * @param string $hook The admin page hook. * @return bool */ function cwp_ai_is_admin_screen( $hook ) { return 'toplevel_page_cloudy-ai-chatbot' === $hook || false !== strpos( (string) $hook, '_page_cloudy-ai-chatbot' ); } /** * Returns the section being viewed. * * @return string */ function cwp_ai_current_tab() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page switch. $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page switch. $step = isset( $_GET['step'] ) ? sanitize_key( wp_unslash( $_GET['step'] ) ) : ''; if ( 'cloudy-ai-chatbot' === $page ) { return '' !== $step ? 'setup' : 'home'; } $name = 0 === strpos( $page, 'cloudy-ai-chatbot-' ) ? substr( $page, strlen( 'cloudy-ai-chatbot-' ) ) : 'home'; return 'setup' === $name ? 'setup' : cwp_ai_route( $name )[0]; } /** * Returns the pane being viewed inside the current section. * * @return string */ function cwp_ai_current_pane() { $section = cwp_ai_current_tab(); $panes = cwp_ai_sections()[ $section ]['panes'] ?? array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page switch. $pane = isset( $_GET['pane'] ) ? sanitize_key( wp_unslash( $_GET['pane'] ) ) : ''; if ( isset( $panes[ $pane ] ) ) { return $pane; } $keys = array_keys( $panes ); return $keys ? $keys[0] : ''; } /** * Returns the sections and their menu labels. * * @return array */ function cwp_ai_admin_tab_labels() { return array_map( function ( $section ) { return (string) $section['label']; }, cwp_ai_sections() ); } add_action( 'admin_init', function () { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only redirect of an old address. $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; if ( 0 !== strpos( $page, 'cloudy-ai-chatbot-' ) ) { return; } $name = substr( $page, strlen( 'cloudy-ai-chatbot-' ) ); if ( 'setup' === $name || isset( cwp_ai_sections()[ $name ] ) ) { return; } wp_safe_redirect( cwp_ai_admin_url( $name ) ); exit; } ); add_action( 'admin_menu', function () { add_menu_page( 'Cloudy AI Chatbot', 'Cloudy AI Chatbot', 'manage_options', 'cloudy-ai-chatbot', 'cwp_ai_render_admin_page', 'dashicons-format-chat', 30 ); foreach ( cwp_ai_admin_tab_labels() as $section => $label ) { add_submenu_page( 'cloudy-ai-chatbot', $label, $label, 'manage_options', cwp_ai_admin_slug( $section ), 'cwp_ai_render_admin_page' ); } $hidden = (array) apply_filters( 'cwp_ai_hidden_screens', array( 'setup', 'vector', 'leads', 'appearance', 'performance', 'transfer', 'usage' ) ); foreach ( $hidden as $name ) { $hook = add_submenu_page( '', 'Cloudy AI Chatbot', 'Cloudy AI Chatbot', 'manage_options', 'cloudy-ai-chatbot-' . $name, 'cwp_ai_render_admin_page' ); if ( $hook ) { add_action( 'load-' . $hook, 'cwp_ai_set_hidden_screen_title' ); } } } ); /** * Gives screens that are not in the menu a page title, which WordPress cannot find for them. */ function cwp_ai_set_hidden_screen_title() { global $title; if ( empty( $title ) ) { $title = 'Cloudy AI Chatbot'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- WordPress reads the admin page title from this global. } } /** * Returns what several screens need, read once per page view. * * @return array */ function cwp_ai_admin_context() { static $ctx = null; if ( null === $ctx ) { $ctx = array( 'qa_pairs' => (array) cwp_ai_knowledge()->all(), ); } return $ctx; } /** * Prints the knowledge screen with only the panels that belong to one pane. * * @param string[] $panels Panel names to show. */ function cwp_ai_render_knowledge_pane( $panels ) { $ctx = cwp_ai_admin_context(); echo '