diff --git a/.monorepo-source.json b/.monorepo-source.json index 19dd882..c219ae7 100644 --- a/.monorepo-source.json +++ b/.monorepo-source.json @@ -1,7 +1,7 @@ { "productId": "funkycommerce-headless", "repository": "coded-letter/superfunky-theme", - "source": "coded-letter/coded-letter-monorepo@47e086c8cc6dd1cbbdbfa426f90ca34ba3123a17", + "source": "coded-letter/coded-letter-monorepo@45fa2d52a63e58ceeb931ddc33ef513e30838b6d", "sourcePath": "workspace/backend/wordpress/themes/free/funkycommerce-headless", "installSlug": "funkycommerce-headless", "kind": "theme", diff --git a/assets/storefront-ui-strings/en.json b/assets/storefront-ui-strings/en.json index 7a3c789..23f05b5 100644 --- a/assets/storefront-ui-strings/en.json +++ b/assets/storefront-ui-strings/en.json @@ -54,6 +54,10 @@ "checkout.field.password": "Password", "checkout.marketing_consent": "Keep me posted about new drops, offers, and restocks by email.", "checkout.guest_notice": "An account will be created with your email address during checkout.", + "checkout.recovery.title": "Cart recovery", + "checkout.recovery.consent": "I consent to abandoned-cart recovery emails.", + "checkout.recovery.legitimate_interest": "We will use your checkout email under legitimate interests to recover your cart.", + "checkout.recovery.details": "Your checkout details are used only to recover this cart and complete your order.", "checkout.delivery.title": "Delivery", "checkout.ship_to_different": "Ship to a different address?", "checkout.shipping_method": "Shipping method", @@ -179,11 +183,12 @@ "auth.breadcrumb.register": "Register", "auth.breadcrumb.forgot": "Forgot password", "auth.desc.login": "Sign in to track orders, manage your wishlist, and check out faster.", - "auth.desc.register": "Join Superfunky for personalized recommendations and faster checkout.", + "auth.desc.register": "Join {brand} for personalized recommendations and faster checkout.", "auth.desc.forgot": "We'll email you a secure link to get back into your account.", "auth.tab.login": "Login", "auth.tab.register": "Register", "auth.tab.forgot": "Forgot", + "auth.mode_selector": "Authentication options", "auth.password_updated": "Your password was updated. Sign in with the new password.", "auth.or_continue": "Or continue with", "auth.continue_with": "Continue with {provider}", @@ -683,6 +688,18 @@ "inquiry.field.email": "Email", "inquiry.field.message": "Message", "inquiry.sending": "Sending…", + "inquiry.validation.name_required": "Enter your name so we know who to reply to.", + "inquiry.validation.email_invalid": "Enter a valid email address.", + "inquiry.validation.message_required": "Enter a message describing what you'd like to know.", + "shortcode.count.post": "{count} post", + "shortcode.count.posts": "{count} posts", + "shortcode.count.product": "{count} product", + "shortcode.count.products": "{count} products", + "shortcode.author.all_posts": "All posts", + "shortcode.tags.title": "Tags", + "shortcode.tags.empty": "No tags matched this shortcode.", + "shortcode.product_tags.title": "Product tags", + "shortcode.product_tags.empty": "No product tags matched this shortcode.", "review.summary_count": "Reviews: {count}", "review.read_more": "Read more", "review.hide": "Hide", diff --git a/assets/storefront-ui-strings/ja.json b/assets/storefront-ui-strings/ja.json index fd0b97c..c89f517 100644 --- a/assets/storefront-ui-strings/ja.json +++ b/assets/storefront-ui-strings/ja.json @@ -54,6 +54,10 @@ "checkout.field.password": "パスワード", "checkout.marketing_consent": "新商品、キャンペーン、再入荷のお知らせをメールで受け取る。", "checkout.guest_notice": "購入手続き中に、メールアドレスを使用してアカウントが作成されます。", + "checkout.recovery.title": "カートの復元", + "checkout.recovery.consent": "放棄されたカートの復元メールを受け取ることに同意します。", + "checkout.recovery.legitimate_interest": "正当な利益に基づき、購入手続きで入力されたメールアドレスをカートの復元に使用します。", + "checkout.recovery.details": "購入手続きの情報は、このカートを復元して注文を完了するためにのみ使用されます。", "checkout.delivery.title": "配送", "checkout.ship_to_different": "別の住所に配送しますか?", "checkout.shipping_method": "配送方法", @@ -179,11 +183,12 @@ "auth.breadcrumb.register": "新規登録", "auth.breadcrumb.forgot": "パスワードを忘れた場合", "auth.desc.login": "ログインすると、注文の追跡、ウィッシュリストの管理、スムーズな購入手続きができます。", - "auth.desc.register": "Superfunkyに登録して、あなた向けのおすすめやスムーズな購入手続きをご利用ください。", + "auth.desc.register": "{brand}に登録して、あなた向けのおすすめやスムーズな購入手続きをご利用ください。", "auth.desc.forgot": "アカウントに再度アクセスするための安全なリンクをメールでお送りします。", "auth.tab.login": "ログイン", "auth.tab.register": "新規登録", "auth.tab.forgot": "パスワードを忘れた場合", + "auth.mode_selector": "認証オプション", "auth.password_updated": "パスワードを更新しました。新しいパスワードでログインしてください。", "auth.or_continue": "または次の方法で続ける", "auth.continue_with": "{provider}で続ける", @@ -683,6 +688,18 @@ "inquiry.field.email": "メールアドレス", "inquiry.field.message": "メッセージ", "inquiry.sending": "送信中…", + "inquiry.validation.name_required": "返信先を確認できるよう、お名前を入力してください。", + "inquiry.validation.email_invalid": "有効なメールアドレスを入力してください。", + "inquiry.validation.message_required": "商品について知りたい内容を入力してください。", + "shortcode.count.post": "{count}件の投稿", + "shortcode.count.posts": "{count}件の投稿", + "shortcode.count.product": "{count}件の商品", + "shortcode.count.products": "{count}件の商品", + "shortcode.author.all_posts": "すべての投稿", + "shortcode.tags.title": "タグ", + "shortcode.tags.empty": "このショートコードに一致するタグはありません。", + "shortcode.product_tags.title": "商品タグ", + "shortcode.product_tags.empty": "このショートコードに一致する商品タグはありません。", "review.summary_count": "レビュー数: {count}", "review.read_more": "続きを読む", "review.hide": "閉じる", diff --git a/assets/storefront-ui-strings/pl.json b/assets/storefront-ui-strings/pl.json index d618e08..f48c4db 100644 --- a/assets/storefront-ui-strings/pl.json +++ b/assets/storefront-ui-strings/pl.json @@ -54,6 +54,10 @@ "checkout.field.password": "Hasło", "checkout.marketing_consent": "Chcę otrzymywać e-maile o nowych produktach, ofertach i wznowieniach.", "checkout.guest_notice": "Konto zostanie utworzone na podany adres e-mail podczas realizacji zamówienia.", + "checkout.recovery.title": "Odzyskiwanie koszyka", + "checkout.recovery.consent": "Wyrażam zgodę na otrzymywanie wiadomości e-mail dotyczących odzyskania porzuconego koszyka.", + "checkout.recovery.legitimate_interest": "Użyjemy adresu e-mail podanego podczas realizacji zamówienia, aby odzyskać koszyk na podstawie naszego uzasadnionego interesu.", + "checkout.recovery.details": "Dane podane podczas realizacji zamówienia służą wyłącznie do odzyskania koszyka i dokończenia zamówienia.", "checkout.delivery.title": "Dostawa", "checkout.ship_to_different": "Wysłać na inny adres?", "checkout.shipping_method": "Metoda dostawy", @@ -179,11 +183,12 @@ "auth.breadcrumb.register": "Rejestracja", "auth.breadcrumb.forgot": "Zapomniane hasło", "auth.desc.login": "Zaloguj się, aby śledzić zamówienia, zarządzać listą życzeń i realizować zakupy szybciej.", - "auth.desc.register": "Dołącz do Superfunky i korzystaj z spersonalizowanych rekomendacji.", + "auth.desc.register": "Dołącz do {brand} i korzystaj ze spersonalizowanych rekomendacji.", "auth.desc.forgot": "Wyślemy Ci bezpieczny link do odzyskania dostępu do konta.", "auth.tab.login": "Logowanie", "auth.tab.register": "Rejestracja", "auth.tab.forgot": "Zapomniane hasło", + "auth.mode_selector": "Opcje uwierzytelniania", "auth.password_updated": "Hasło zostało zaktualizowane. Zaloguj się nowym hasłem.", "auth.or_continue": "Lub kontynuuj z", "auth.continue_with": "Kontynuuj z {provider}", @@ -683,6 +688,18 @@ "inquiry.field.email": "E-mail", "inquiry.field.message": "Wiadomość", "inquiry.sending": "Wysyłanie…", + "inquiry.validation.name_required": "Podaj imię i nazwisko, abyśmy wiedzieli, komu odpowiedzieć.", + "inquiry.validation.email_invalid": "Podaj prawidłowy adres e-mail.", + "inquiry.validation.message_required": "Wpisz wiadomość z pytaniem o produkt.", + "shortcode.count.post": "{count} wpis", + "shortcode.count.posts": "{count} wpisów", + "shortcode.count.product": "{count} produkt", + "shortcode.count.products": "{count} produktów", + "shortcode.author.all_posts": "Wszystkie wpisy", + "shortcode.tags.title": "Tagi", + "shortcode.tags.empty": "Brak tagów pasujących do tego shortcode'u.", + "shortcode.product_tags.title": "Tagi produktów", + "shortcode.product_tags.empty": "Brak tagów produktów pasujących do tego shortcode'u.", "review.summary_count": "Liczba opinii: {count}", "review.read_more": "Czytaj więcej", "review.hide": "Ukryj", diff --git a/functions.php b/functions.php index fe7c530..6b5b3d9 100644 --- a/functions.php +++ b/functions.php @@ -9,7 +9,7 @@ exit; } -define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.19' ); +define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.20' ); /** * Whether Superfunky Pro is active and licensed. @@ -251,6 +251,7 @@ function funkycommerce_headless_content_shortcodes() { 'sticky_posts', 'tags', 'product-tags', + 'product_tags', 'authors', 'reviews', 'comments', @@ -549,6 +550,7 @@ function funkycommerce_content_shortcode_schemas() { 'sticky-posts' => $sticky_posts_schema, 'sticky_posts' => $sticky_posts_schema, 'tags' => array( + 'type' => array( 'default' => 'post', 'enum' => array( 'post', 'product' ) ), 'layout' => array( 'default' => 'pills', 'enum' => array( 'pills', 'cards', 'compact' ) ), 'offset' => funkycommerce_collection_shortcode_offset_definition(), 'limit' => array( 'default' => 24, 'type' => 'integer', 'min' => 1, 'max' => 100 ), @@ -566,6 +568,15 @@ function funkycommerce_content_shortcode_schemas() { 'order' => array( 'default' => 'asc', 'enum' => array( 'asc', 'desc' ) ), 'title' => array( 'default' => __( 'Product tags', 'funkycommerce-headless' ) ), ), + 'product_tags' => array( + 'layout' => array( 'default' => 'pills', 'enum' => array( 'pills', 'cards', 'compact' ) ), + 'offset' => funkycommerce_collection_shortcode_offset_definition(), + 'limit' => array( 'default' => 24, 'type' => 'integer', 'min' => 1, 'max' => 100 ), + 'include' => array( 'default' => '' ), + 'orderby' => array( 'default' => 'name', 'enum' => array( 'name', 'count', 'include' ) ), + 'order' => array( 'default' => 'asc', 'enum' => array( 'asc', 'desc' ) ), + 'title' => array( 'default' => __( 'Product tags', 'funkycommerce-headless' ) ), + ), 'authors' => array( 'layout' => array( 'default' => 'cards', 'enum' => array( 'cards', 'compact' ) ), 'offset' => funkycommerce_collection_shortcode_offset_definition(), diff --git a/inc/artifact-renderer.php b/inc/artifact-renderer.php index bdd788d..8bb2efb 100644 --- a/inc/artifact-renderer.php +++ b/inc/artifact-renderer.php @@ -28,6 +28,7 @@ private static function public_shortcode_renderers() { 'grid' => 'funkycommerce_native_render_grid', 'tags' => 'funkycommerce_native_render_tags', 'product-tags' => 'funkycommerce_native_render_product_tags', + 'product_tags' => 'funkycommerce_native_render_product_tags', 'authors' => 'funkycommerce_native_render_authors', 'reviews' => 'funkycommerce_native_render_reviews', 'comments' => 'funkycommerce_native_render_comments', diff --git a/inc/control-center-schema.php b/inc/control-center-schema.php index f37a0a0..6b14b10 100644 --- a/inc/control-center-schema.php +++ b/inc/control-center-schema.php @@ -274,16 +274,17 @@ function funkycommerce_layout_control_fields() { 'layout_header_sticky' => $toggle( __( 'Sticky header', 'funkycommerce-headless' ), 'yes', 'header_sticky' ), 'layout_header_search_variant' => $select( __( 'Header search layout', 'funkycommerce-headless' ), 'full-width', array( 'full-width', 'expandable' ), 'header_search_variant' ), 'layout_header_logo_variant' => $select( __( 'Header logo layout', 'funkycommerce-headless' ), 'text-image', array( 'text', 'image', 'text-image' ), 'header_logo_variant' ), - 'layout_header_arrangement' => $select( __( 'Header content arrangement', 'funkycommerce-headless' ), 'classic', array( 'classic', 'single-row', 'centered' ), 'header_arrangement' ), + 'layout_header_arrangement' => $select( __( 'Header content arrangement', 'funkycommerce-headless' ), 'classic', array( 'classic', 'single-row', 'centered', 'island' ), 'header_arrangement' ), 'layout_cart_trigger_variant' => $select( __( 'Header cart behavior', 'funkycommerce-headless' ), 'drawer', array( 'drawer', 'dropdown' ), 'cart_trigger_variant' ), 'layout_show_cart_promoted_product' => $toggle( __( 'Show promoted product in empty cart', 'funkycommerce-headless' ), 'yes', 'show_cart_drawer_promoted_product' ), 'layout_show_all_cart_promoted_products' => $toggle( __( 'Show all featured products in empty cart', 'funkycommerce-headless' ), 'no', 'show_all_cart_promoted_products' ), 'layout_show_footer' => $toggle( __( 'Show footer', 'funkycommerce-headless' ), 'yes', 'show_footer' ), - 'layout_footer_columns' => $select( __( 'Footer columns', 'funkycommerce-headless' ), 'grid-4', array( 'grid-1', 'grid-2-wide', 'grid-4', 'grid-5', 'grid-6', 'grid-7', 'accordion-single' ), 'footer_columns_layout' ), + 'layout_footer_columns' => $select( __( 'Footer columns', 'funkycommerce-headless' ), 'grid-4', array( 'grid-1', 'grid-2-wide', 'grid-3', 'grid-4', 'grid-5', 'grid-6', 'grid-7', 'accordion-single' ), 'footer_columns_layout' ), 'layout_footer_newsletter' => $select( __( 'Footer newsletter layout', 'funkycommerce-headless' ), 'banner', array( 'banner', 'centered', 'image-bg' ), 'footer_newsletter_layout' ), 'layout_show_footer_newsletter' => $toggle( __( 'Show footer newsletter', 'funkycommerce-headless' ), 'yes', 'show_footer_newsletter' ), 'layout_footer_assistant' => $select( __( 'Footer assistant / player layout', 'funkycommerce-headless' ), 'side-by-side', array( 'side-by-side', 'tabbed', 'stacked' ), 'footer_assistant_layout' ), + 'layout_footer_feature_layout' => $select( __( 'Footer feature combination', 'funkycommerce-headless' ), 'separate', array( 'separate', 'newsletter-spotify', 'newsletter-assistant', 'assistant-spotify', 'none' ), 'footer_feature_layout' ), 'layout_footer_logo_variant' => $select( __( 'Footer logo layout', 'funkycommerce-headless' ), 'text-image', array( 'text', 'image', 'text-image' ), 'footer_logo_variant' ), 'layout_footer_bottom_bar' => $select( __( 'Footer bottom bar layout', 'funkycommerce-headless' ), 'split', array( 'split', 'centered' ), 'footer_bottom_bar_layout' ), 'layout_footer_extra_wrapper' => $select( __( 'Footer extra wrapper layout', 'funkycommerce-headless' ), 'inline', array( 'inline', 'full-bleed' ), 'footer_extra_wrapper_layout' ), diff --git a/inc/control-center.php b/inc/control-center.php index f252e5a..b02a220 100644 --- a/inc/control-center.php +++ b/inc/control-center.php @@ -31,6 +31,7 @@ function funkycommerce_control_center_settings() { } } $saved = (array) get_option( 'funkycommerce_control_center', array() ); + $saved = funkycommerce_recover_corrupted_header_visibility( $saved ); if ( ! array_key_exists( 'custom_css', $saved ) ) { $saved['custom_css'] = (string) get_option( 'funkycommerce_custom_css', '' ); @@ -61,6 +62,51 @@ function funkycommerce_control_center_settings() { return array_merge( $defaults, $saved ); } +/** + * Recover header controls corrupted by section-specific settings saves. + * + * Earlier releases initialized fields omitted by the submitted section as empty + * values. The numeric minima and an entirely disabled header form a sufficiently + * specific fingerprint to repair without replacing intentional layout choices. + * + * @param array $saved Raw Control Center settings. + * @return array + */ +function funkycommerce_recover_corrupted_header_visibility( $saved ) { + $recovery_option = 'funkycommerce_header_visibility_recovery_1'; + if ( 'yes' === get_option( $recovery_option, 'no' ) ) { + return $saved; + } + + $header_keys = array( + 'layout_show_header_logo', + 'layout_show_header_search_icon', + 'layout_show_header_language_switcher', + 'layout_show_header_currency_switcher', + 'layout_show_header_dark_mode_toggle', + 'layout_show_header_account_link', + 'layout_show_header_reading_list_link', + 'layout_show_header_wishlist_link', + 'layout_show_header_cart_icon', + 'layout_show_header_publish_button', + ); + $has_corruption_fingerprint = '960' === (string) ( $saved['layout_theme_max_width_px'] ?? '' ) + && '0' === (string) ( $saved['layout_theme_radius_px'] ?? '' ) + && '1' === (string) ( $saved['layout_newsletter_popup_cooldown_days'] ?? '' ) + && count( $header_keys ) === count( array_filter( $header_keys, static fn( $key ) => 'no' === ( $saved[ $key ] ?? null ) ) ); + + add_option( $recovery_option, 'yes', '', false ); + if ( ! $has_corruption_fingerprint ) { + return $saved; + } + + foreach ( $header_keys as $key ) { + $saved[ $key ] = 'yes'; + } + update_option( 'funkycommerce_control_center', $saved, false ); + return $saved; +} + /** * Return a normalized public layout object from schema defaults and saved values. * @@ -493,7 +539,7 @@ function funkycommerce_sanitize_control_center( $input ) { $output[ $key ] = $previous[ $key ]; continue; } - $value = $input[ $key ] ?? null; + $value = array_key_exists( $key, $input ) ? $input[ $key ] : ( $field['default'] ?? null ); $output[ $key ] = funkycommerce_sanitize_control_field( $key, $field, $value, $previous[ $key ] ?? ( $field['default'] ?? '' ) ); } $output['layout_schema_version'] = 1; diff --git a/inc/native-shortcodes.php b/inc/native-shortcodes.php index d1ffdfb..577ffc6 100644 --- a/inc/native-shortcodes.php +++ b/inc/native-shortcodes.php @@ -73,6 +73,7 @@ function funkycommerce_register_native_shortcodes() { add_shortcode( 'grid', 'funkycommerce_native_render_grid' ); add_shortcode( 'tags', 'funkycommerce_native_render_tags' ); add_shortcode( 'product-tags', 'funkycommerce_native_render_product_tags' ); + add_shortcode( 'product_tags', 'funkycommerce_native_render_product_tags' ); add_shortcode( 'authors', 'funkycommerce_native_render_authors' ); add_shortcode( 'reviews', 'funkycommerce_native_render_reviews' ); add_shortcode( 'comments', 'funkycommerce_native_render_comments' ); @@ -1052,6 +1053,12 @@ function funkycommerce_native_render_categories( $atts ) { if ( $include ) { $query_args['slug'] = $include; } + if ( function_exists( 'pll_current_language' ) ) { + $language = sanitize_key( (string) pll_current_language( 'slug' ) ); + if ( $language ) { + $query_args['lang'] = $language; + } + } $terms = get_terms( $query_args ); if ( is_wp_error( $terms ) || empty( $terms ) ) { @@ -1291,6 +1298,9 @@ function funkycommerce_native_render_grid( $atts ) { */ function funkycommerce_native_render_tags( $atts ) { $a = funkycommerce_native_prepare_content_attributes( 'tags', $atts ); + if ( 'product' === ( $a['type'] ?? 'post' ) ) { + return funkycommerce_native_render_product_tags( $atts ); + } $include = ! empty( $a['include'] ) ? funkycommerce_native_csv_terms( $a['include'] ) : array(); diff --git a/package-lock.json b/package-lock.json index bc1069a..ac386e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "funkycommerce-headless-theme", - "version": "1.2.19", + "version": "1.2.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "funkycommerce-headless-theme", - "version": "1.2.19", + "version": "1.2.20", "devDependencies": { "autoprefixer": "^10.4.20", "esbuild": "^0.28.2", diff --git a/package.json b/package.json index 8c26d84..ee14d35 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "funkycommerce-headless-theme", "private": true, - "version": "1.2.19", + "version": "1.2.20", "description": "Tailwind/build tooling for the FunkyCommerce Headless WordPress theme's native frontend assets (block templates, template parts, loader, and Spotify slot).", "scripts": { "build:css": "tailwindcss -i ./assets/css/theme-source.css -o ./assets/dist/theme.css --minify", diff --git a/readme.txt b/readme.txt index 6855740..8cc30cb 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: headless, woocommerce, wpgraphql, full-site-editing Requires at least: 6.7 Tested up to: 6.8 Requires PHP: 7.4 -Stable tag: 1.2.19 +Stable tag: 1.2.20 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -15,6 +15,13 @@ Control Center, the theme also ships a complete native WordPress rendering path "Native frontend theme" below) with accessible header/footer/navigation templates and core front/home/singular/archive/search/404 routes styled to match the storefront. +== 1.2.20 highlights == + +* Recovers header controls incorrectly disabled by section-specific Control Center saves and preserves schema defaults for newly introduced fields. +* Adds true single-row and floating-island headers, three-column footers, and configurable newsletter, assistant, and Spotify combinations. +* Improves multilingual blog and product-tag shortcodes, frontend favicon handoff, archive indexing, translations, and product/hero presentation. +* Uses cart-scoped payment, shipping, and tax availability at checkout, gates translated cart recovery on its companion plugin, and blocks unavailable products from cart actions. + == 1.2.19 highlights == * Preserves product image aspect ratios in galleries and lightboxes and serves linked media-library PDFs through the storefront domain. diff --git a/style.css b/style.css index 379f130..9c0fec2 100644 --- a/style.css +++ b/style.css @@ -7,7 +7,7 @@ Description: A minimal, content-first block theme for polished WordPress preview Requires at least: 6.7 Tested up to: 6.8 Requires PHP: 7.4 -Version: 1.2.19 +Version: 1.2.20 Update URI: https://github.com/coded-letter/superfunky-theme License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html