plugin = $plugin;
$this->config = $plugin->config();
}
/**
* Settings API registration.
*/
private function render_subscription() {
if ('development' === Entitlements::edition()) { return; }
echo ' ' . esc_html(Entitlements::can_use_pro() ? __('Embodied Assistant Pro', 'sentify-embodied-assistant') : __('Embodied Assistant Free', 'sentify-embodied-assistant')) . ' ' . esc_html__('Pro adds shopping actions and an expanded avatar selection. AI-provider usage is billed separately by your provider.', 'sentify-embodied-assistant') . ' ' . esc_html__('Learn more about Pro', 'sentify-embodied-assistant') . ' ' . esc_html__('Upgrades are available on our website. After purchasing, install the Pro plugin from your purchase account. Your settings are kept.', 'sentify-embodied-assistant') . '
' . esc_html__('Upgrade to Pro', 'sentify-embodied-assistant') . '
'; echo '' . esc_html__('After purchasing, install the Pro plugin from your purchase account. Your settings are kept.', 'sentify-embodied-assistant') . '
'; } else { echo '' . esc_html__('Preview build: the Pro upgrade link is not configured.', 'sentify-embodied-assistant') . '
'; } } echo ''; } /** Settings sections share one form and are linked from WordPress's sidebar. */ public static function sections() { return array( 'general' => __('General', 'sentify-embodied-assistant'), 'assistant' => __('Assistant', 'sentify-embodied-assistant'), 'ai-provider' => __('AI provider', 'sentify-embodied-assistant'), 'capabilities' => __('Capabilities', 'sentify-embodied-assistant'), 'widget' => __('Widget', 'sentify-embodied-assistant'), 'usage' => __('Usage', 'sentify-embodied-assistant'), ); } public static function page_slug($section) { return 'general' === $section ? self::PAGE : self::PAGE . '-' . $section; } public static function active_section() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only allowlisted navigation. $page = isset($_GET['page']) && is_string($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : self::PAGE; foreach (self::sections() as $section => $label) { if ('general' !== $section && self::page_slug($section) === $page) { return $section; } } // Keep bookmarked links from the former tab navigation working. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only allowlisted navigation. $tab = isset($_GET['tab']) && is_string($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'general'; return self::PAGE === $page && isset(self::sections()[$tab]) ? $tab : 'general'; } public static function highlight_submenu($submenu_file, $parent_file) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only menu highlighting. $page = isset($_GET['page']) && is_string($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : ''; $pages = array_map(array(__CLASS__, 'page_slug'), array_keys(self::sections())); return self::PAGE === $parent_file && in_array($page, $pages, true) ? self::page_slug(self::active_section()) : $submenu_file; } public function register() { register_setting( self::GROUP, ConfigRepository::OPTION_KEY, array( 'type' => 'array', 'sanitize_callback' => array($this->config, 'sanitize'), 'default' => $this->config->defaults(), ) ); add_settings_section('embodied_assistant_general', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_agent', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_avatar', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_realtime', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_deployment', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_tools', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_limits', '', '__return_false', self::PAGE); add_settings_section('embodied_assistant_privacy', '', '__return_false', self::PAGE); add_action('wp_ajax_embodied_assistant_list_avatars', array($this, 'handle_list_avatars')); add_action('wp_ajax_embodied_assistant_import_avatar', array($this, 'handle_import_avatar')); } /** * Settings page HTML. */ public function render_page() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to manage this plugin.', 'sentify-embodied-assistant')); } $settings = $this->config->get(); $provider = $settings['realtime']['provider'] ?? 'openai'; $credential_status = $this->plugin->realtime_credentials()->readiness($provider); $tabs = self::sections(); $active_tab = self::active_section(); ?>
|
|
|
|
|
|
|
|
|
|
$color) : ?>
|
|
' . esc_html($message); if ($url) { echo ' ' . esc_html__('Explore Pro', 'sentify-embodied-assistant') . ''; } echo '
'; } private function render_tools(array $s) { $tool_help = array( 'avatar-set-emotion' => __('Let the avatar express emotions during a conversation.', 'sentify-embodied-assistant'), 'avatar-set-view' => __('Adjust how much of the avatar is visible.', 'sentify-embodied-assistant'), 'avatar-play-animation' => __('Enable optional animation clips and allow visitors’ browsers to download them directly from Sentifyd Azure storage. Requests include IP address and browser metadata. Clips are not copied to your site. Voice, lipsync and built-in gestures work without this tool.', 'sentify-embodied-assistant'), 'avatar-play-gesture' => __('Let the avatar use gestures during a conversation.', 'sentify-embodied-assistant'), 'avatar-look-at' => __('Control where the avatar looks and when it makes eye contact.', 'sentify-embodied-assistant'), 'wordpress-search-posts' => __('Find published posts and pages on your site.', 'sentify-embodied-assistant'), 'wordpress-get-post' => __('Read a published post or page to answer questions.', 'sentify-embodied-assistant'), 'wordpress-get-menu' => __('Use your site menu to help visitors find their way.', 'sentify-embodied-assistant'), 'wordpress-get-support-options' => __('Share configured support contacts and find the contact page.', 'sentify-embodied-assistant'), 'wordpress-get-store-info' => __('Share store details such as contact information, opening hours, and address.', 'sentify-embodied-assistant'), 'wordpress-navigate' => __('Take visitors to a page, their cart, or checkout.', 'sentify-embodied-assistant'), 'wordpress-list-pages' => __('Find the pages available on your site.', 'sentify-embodied-assistant'), 'woocommerce-search-products' => __('Find products by name, category, or keyword.', 'sentify-embodied-assistant'), 'woocommerce-get-brands' => __('Help visitors browse product brands.', 'sentify-embodied-assistant'), 'woocommerce-get-tags' => __('Find product tags to help visitors explore your catalog.', 'sentify-embodied-assistant'), 'woocommerce-get-attributes' => __('Help visitors explore product options such as size and color.', 'sentify-embodied-assistant'), 'woocommerce-get-product' => __('Answer questions about product details, prices, options, and stock.', 'sentify-embodied-assistant'), 'woocommerce-get-categories' => __('Help visitors browse product categories.', 'sentify-embodied-assistant'), 'woocommerce-get-product-reviews' => __('Share published customer reviews for a product.', 'sentify-embodied-assistant'), 'woocommerce-get-related-products' => __('Suggest similar products from your catalog.', 'sentify-embodied-assistant'), 'woocommerce-get-highlights' => __('Find featured products, sale items, and best sellers.', 'sentify-embodied-assistant'), 'woocommerce-get-cart' => __('Check the visitor\'s shopping cart and totals.', 'sentify-embodied-assistant'), 'woocommerce-add-to-cart' => __('Add products to the visitor\'s shopping cart.', 'sentify-embodied-assistant'), 'woocommerce-update-cart' => __('Change the quantity of an item in the shopping cart.', 'sentify-embodied-assistant'), 'woocommerce-remove-from-cart' => __('Remove items from the visitor\'s shopping cart.', 'sentify-embodied-assistant'), 'woocommerce-apply-coupon' => __('Apply a discount code to the visitor\'s shopping cart.', 'sentify-embodied-assistant'), 'woocommerce-remove-coupon' => __('Remove a discount code from the visitor\'s shopping cart.', 'sentify-embodied-assistant'), ); $registry = $this->plugin->tool_registry(); try { $declarations = $registry ? $registry->catalog() : array(); $catalog = $registry ? $registry->runtime_definitions() : array(); } catch (\Throwable $e) { $declarations = array(); $catalog = array(); } $previews = $this->pro_tool_previews(); $can_confirm_cart = false; foreach ($catalog as $def) { if ('woocommerce' === ($def['availability'] ?? '') && !empty($def['confirm_mutation']) && class_exists('WooCommerce')) { $can_confirm_cart = true; break; } } // Preserve contact details even when the native support Ability is unavailable. if (!isset($catalog['embodied-assistant/wordpress-get-support-options'])) { foreach ($s['support'] as $field => $value) { printf('', esc_attr($field), esc_attr($value)); } } if (empty($declarations)) { echo '' . esc_html__('Tool catalog missing (build assets not installed). Install the release package.', 'sentify-embodied-assistant') . '
' . esc_html__('Some catalog operations are unavailable because their required service or native Ability is missing. Saved selections are retained; activate WooCommerce or the appropriate Pro license to use them.', 'sentify-embodied-assistant') . '
'; } if (array_diff($unknown, array_keys($previews))) { echo '' . esc_html__('Some saved tools are unavailable in this edition. Their selections are retained but they cannot run.', 'sentify-embodied-assistant') . '
'; } $groups = array( 'avatar' => array('label' => __('Avatar controls', 'sentify-embodied-assistant'), 'tools' => array()), 'woocommerce' => array('label' => __('WooCommerce shop assistant', 'sentify-embodied-assistant'), 'tools' => array()), 'wordpress' => array('label' => __('WordPress tools', 'sentify-embodied-assistant'), 'tools' => array()), ); // Missing dependencies stay visible, while runtime discovery remains authoritative. $display_catalog = $declarations; if (!Entitlements::can_use_pro()) { foreach ($previews as $name => $label) { if (!isset($display_catalog[$name])) { $display_catalog[$name] = array('label' => $label, 'availability' => 'woocommerce'); } } } foreach ($display_catalog as $name => $def) { $group = $def['availability'] ?? 'wordpress'; if (!isset($groups[$group])) { $group = 'wordpress'; } $groups[$group]['tools'][$name] = $def; } ?>|
render_pro_hint(__('Tools marked Pro are available with an upgrade. WooCommerce is required for all shop tools.', 'sentify-embodied-assistant'));
} ?>
|