'', 'enabled' => 0, 'remember' => 0, 'consent_required' => 0, 'exclude_commerce' => 1, 'excluded_paths' => '', ) ); } /** Only public installation identifiers are accepted; never arbitrary markup. */ function dashe_chat_valid_key( $key ) { return is_string( $key ) && 1 === preg_match( '/\Apk_live_[A-Za-z0-9]{16,64}\z/', $key ); } /** Validate the Settings API submission; its options.php handler enforces capability and nonce. */ function dashe_chat_sanitize( $input ) { $old = dashe_chat_options(); $input = is_array( $input ) ? $input : array(); $key = isset( $input['public_key'] ) && is_string( $input['public_key'] ) ? trim( $input['public_key'] ) : ''; if ( ( isset( $input['public_key'] ) && ! is_string( $input['public_key'] ) ) || ( '' !== $key && ! dashe_chat_valid_key( $key ) ) ) { add_settings_error( 'dashe_chat_settings', 'dashe_chat_key', __( 'Use only the public widget key beginning with pk_live_. Do not paste a script, password or API secret. Your previous settings were kept.', 'dashe-chat' ) ); return $old; } $raw_paths = isset( $input['excluded_paths'] ) && is_string( $input['excluded_paths'] ) ? $input['excluded_paths'] : ''; $paths = array(); foreach ( preg_split( '/\r\n|\r|\n/', $raw_paths ) as $line ) { $line = trim( $line ); if ( '' === $line ) { continue; } // A literal URL path, optionally ending in /*. No query strings or remote URLs. if ( strlen( $line ) > 300 || ! preg_match( '~\A/(?!/)[A-Za-z0-9/_%.\-]*(?:\*)?\z~', $line ) || ( false !== strpos( $line, '*' ) && '/*' !== substr( $line, -2 ) ) || false !== strpos( $line, '..' ) ) { add_settings_error( 'dashe_chat_settings', 'dashe_chat_paths', __( 'Enter one path per line, such as /private or /members/*. Use paths without a domain, query string or fragment. Your previous settings were kept.', 'dashe-chat' ) ); return $old; } $paths[] = $line; } if ( count( $paths ) > 100 ) { add_settings_error( 'dashe_chat_settings', 'dashe_chat_path_limit', __( 'Use no more than 100 excluded paths. Your previous settings were kept.', 'dashe-chat' ) ); return $old; } if ( ! empty( $input['enabled'] ) && '' === $key ) { add_settings_error( 'dashe_chat_settings', 'dashe_chat_missing_key', __( 'Add your public widget key before enabling Dashe.', 'dashe-chat' ) ); } return array( 'public_key' => $key, 'enabled' => ! empty( $input['enabled'] ) && '' !== $key ? 1 : 0, 'remember' => ! empty( $input['remember'] ) ? 1 : 0, 'consent_required' => ! empty( $input['consent_required'] ) ? 1 : 0, 'exclude_commerce' => ! empty( $input['exclude_commerce'] ) ? 1 : 0, 'excluded_paths' => implode( "\n", array_unique( $paths ) ), ); } /** Register only our own settings. */ function dashe_chat_register_settings() { register_setting( 'dashe_chat', 'dashe_chat_settings', array( 'type' => 'array', 'sanitize_callback' => 'dashe_chat_sanitize', 'show_in_rest' => false ) ); if ( function_exists( 'wp_add_privacy_policy_content' ) ) { wp_add_privacy_policy_content( 'Dashe Chat', '

' . esc_html__( 'Suggested text — adapt to your configuration: We use Dashe to provide website chat and, when enabled, appointment booking. When the widget loads, your browser connects to Dashe and sends connection information, page context and chat activity. Information you submit in chat or booking is processed by Dashe for our business. The widget uses browser session storage to resume a chat. If we enable returning-browser recognition, it also stores a random identifier in local storage. Optional calls require your separate microphone or camera permission. Ask us about the settings, retention and service providers that apply to this website.', 'dashe-chat' ) . '

' ); } } add_action( 'admin_init', 'dashe_chat_register_settings' ); /** Add a conventional, narrowly scoped settings screen. */ function dashe_chat_admin_menu() { add_options_page( __( 'Dashe Chat', 'dashe-chat' ), __( 'Dashe Chat', 'dashe-chat' ), 'manage_options', 'dashe-chat', 'dashe_chat_settings_page' ); } add_action( 'admin_menu', 'dashe_chat_admin_menu' ); /** Link directly from the plugin list. */ function dashe_chat_action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'dashe-chat' ) . '' ); return $links; } add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'dashe_chat_action_links' ); /** Render fields with WordPress styles; no remote assets or iframe-based sign-in. */ function dashe_chat_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $options = dashe_chat_options(); ?>

  1. —

window.dasheChatConsent = true;
document.dispatchEvent(new Event('dashe:consent'));

· · ·

'defer', 'in_footer' => true ) ); } } add_action( 'wp_enqueue_scripts', 'dashe_chat_enqueue' ); /** Pass escaped configuration without inline script or arbitrary remote script URLs. */ function dashe_chat_script_tag( $tag, $handle ) { if ( 'dashe-chat-loader' !== $handle ) { return $tag; } $options = dashe_chat_options(); $html = new WP_HTML_Tag_Processor( $tag ); if ( $html->next_tag( 'SCRIPT' ) ) { $html->set_attribute( 'data-dashe-wp-key', $options['public_key'] ); $html->set_attribute( 'data-dashe-wp-remember', empty( $options['remember'] ) ? 'false' : 'true' ); $html->set_attribute( 'data-dashe-wp-consent', empty( $options['consent_required'] ) ? 'false' : 'true' ); } return $html->get_updated_html(); } add_filter( 'script_loader_tag', 'dashe_chat_script_tag', 10, 2 ); require_once __DIR__ . '/includes/seo.php';