' . esc_html__( 'Settings saved.', 'addon-boss' ) . '
'; } // Save the storefront Colours (free: appearance ships with every build). // Its own form and nonce, so a free install with no uploads engine still // saves colours. addon_boss_sanitize_colours is the boundary: hex per // known token or nothing stores. if ( isset( $_POST['addon_boss_colours_nonce'] ) && current_user_can( 'manage_woocommerce' ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['addon_boss_colours_nonce'] ) ), 'addon_boss_colours_settings' ) ) { $bb_raw_cols = map_deep( wp_unslash( (array) ( $_POST['addon_boss_colours'] ?? array() ) ), 'sanitize_text_field' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- leaf-sanitised here, hex-validated in the sanitiser. update_option( 'addon_boss_colours', addon_boss_sanitize_colours( (array) $bb_raw_cols ) ); echo '' . esc_html__( 'Settings saved.', 'addon-boss' ) . '
' . esc_html__( 'Product options, add-ons and personalisation on any WooCommerce product.', 'addon-boss' ) . '
'; addon_boss_render_colours_card(); echo '' . esc_html__( 'When customers upload artwork or files on a product, decide how those files are organised, delivered and named for your shop.', 'addon-boss' ) . '
'; $folder = '1' === (string) get_option( 'addon_boss_uploads_folder_per_order', '1' ); $attach = '1' === (string) get_option( 'addon_boss_uploads_attach_email', '1' ); $pattern = (string) get_option( 'addon_boss_uploads_rename_pattern', '' ); $tokens = function_exists( 'addon_boss_upload_rename_tokens' ) ? addon_boss_upload_rename_tokens() : array(); echo ''; ?> ( function () { var input = document.getElementById( 'bb-up-rename' ); var prev = document.getElementById( 'bb-up-rename-preview' ); if ( ! input || ! prev ) { return; } var egs = {}; document.querySelectorAll( '.bb-up-chip' ).forEach( function ( b ) { egs[ b.getAttribute( 'data-token' ) ] = b.getAttribute( 'data-eg' ); } ); function render() { var p = ( input.value || '' ).trim(); if ( '' === p ) { prev.textContent = 'logo.png'; return; } var name = p.replace( /\{[a-z]+\}/g, function ( m ) { return undefined !== egs[ m ] ? egs[ m ] : ''; } ); name = name.replace( /[\/\\:*?"<>|]+/g, '-' ).replace( /[\s_-]{2,}/g, '-' ).replace( /^[-\s_.]+|[-\s_.]+$/g, '' ); prev.textContent = ( name || 'logo' ) + '.png'; } document.querySelectorAll( '.bb-up-chip' ).forEach( function ( b ) { b.addEventListener( 'click', function () { var t = b.getAttribute( 'data-token' ); input.value = ( input.value && ! /[-_ ]$/.test( input.value ) ) ? input.value + '-' + t : input.value + t; render(); input.focus(); } ); } ); var clr = document.getElementById( 'bb-up-rename-clear' ); if ( clr ) { clr.addEventListener( 'click', function () { input.value = ''; render(); } ); } input.addEventListener( 'input', render ); // The scheme dropdown fills the one stored pattern; Build your own // opens the token builder over whatever is currently stored. var scheme = document.getElementById( 'bb-up-scheme' ); var builder = document.getElementById( 'bb-up-builder' ); if ( scheme && builder ) { scheme.addEventListener( 'change', function () { var opt = scheme.selectedOptions && scheme.selectedOptions[ 0 ]; if ( ! opt ) { return; } if ( 'custom' === opt.value ) { builder.hidden = false; input.focus(); render(); return; } builder.hidden = true; input.value = opt.getAttribute( 'data-pattern' ) || ''; render(); } ); } render(); } )(); '; } /** * The storefront Colours card: four overrides over the skin tokens, each a * picker + hex box + Clear. Free (appearance ships with every build), its * own form and nonce. The skin's three layers are stated in the lede so * the merchant knows what leaving a colour empty means: neutral defaults * that work anywhere, a block theme's palette flowing in where one is * published, and these choices over both. */ function addon_boss_render_colours_card() { $bb_rows = array( 'accent' => array( 'title' => __( 'Highlights and prompts', 'addon-boss' ), 'desc' => __( 'Paints the Choose prompts, the add-another button and the panel\'s other highlights. Out of the box it follows a block theme\'s own accent, with the house coral everywhere else.', 'addon-boss' ), 'default' => '#fd4730', 'placeholder' => __( 'Follows your theme', 'addon-boss' ), ), 'sel' => array( 'title' => __( 'Selected choice', 'addon-boss' ), 'desc' => __( 'The fill behind a picked button and the ring around a picked swatch. Out of the box it uses the theme\'s own text colour, which reads clearly on any store.', 'addon-boss' ), 'default' => '#16181d', 'placeholder' => __( 'Follows your theme', 'addon-boss' ), ), 'receipt' => array( 'title' => __( 'Receipt line', 'addon-boss' ), 'desc' => __( 'The rule across the top of the price breakdown, where the options hand over to the total.', 'addon-boss' ), 'default' => '#16181d', 'placeholder' => __( 'Follows your theme', 'addon-boss' ), ), 'wrap' => array( 'title' => __( 'Options background', 'addon-boss' ), 'desc' => __( 'A soft tint behind the whole options area, drawn as its own panel. Out of the box it is transparent, so your page shows through.', 'addon-boss' ), 'default' => '#f4f3f0', 'placeholder' => __( 'Transparent', 'addon-boss' ), ), ); $stored = addon_boss_sanitize_colours( (array) get_option( 'addon_boss_colours', array() ) ); echo ''; ?> document.querySelectorAll( '.bb-col-row' ).forEach( function ( row ) { var pick = row.querySelector( '.bb-col-pick' ); var hex = row.querySelector( '.bb-col-hex' ); var clr = row.querySelector( '.bb-col-clear' ); if ( ! pick || ! hex ) { return; } pick.addEventListener( 'input', function () { hex.value = pick.value; } ); hex.addEventListener( 'input', function () { var v = hex.value.trim(); if ( /^#[0-9a-fA-F]{6}$/.test( v ) ) { pick.value = v; } } ); if ( clr ) { clr.addEventListener( 'click', function () { hex.value = ''; pick.value = pick.getAttribute( 'data-default' ) || '#ffffff'; } ); } } );