diff --git a/.monorepo-source.json b/.monorepo-source.json index c219ae7..40519c4 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@45fa2d52a63e58ceeb931ddc33ef513e30838b6d", + "source": "coded-letter/coded-letter-monorepo@165fb6d20361bfd8a4811cec30febd78cefa3d2d", "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 23f05b5..19681ff 100644 --- a/assets/storefront-ui-strings/en.json +++ b/assets/storefront-ui-strings/en.json @@ -1,4 +1,8 @@ { + "layout.search.style": "Search style", + "layout.search.full_width": "Full-width bar", + "layout.search.expandable": "Icon → expands", + "layout.search.overlay": "Full-screen overlay", "cart.title": "Your cart", "cart.aria": "Shopping cart", "cart.close": "Close cart", @@ -682,6 +686,9 @@ "navigation.back_to_top": "Back to top", "nav.shop": "Shop", "nav.blog": "Blog", + "inquiry.heading": "Product inquiry", + "inquiry.button": "Ask about this product", + "inquiry.copy": "Send us a message and we will follow up with availability and pricing.", "inquiry.error": "The inquiry could not be sent. Please try again.", "inquiry.success": "Your inquiry has been sent. We'll follow up by email shortly.", "inquiry.field.name": "Name", diff --git a/assets/storefront-ui-strings/ja.json b/assets/storefront-ui-strings/ja.json index c89f517..6332fc5 100644 --- a/assets/storefront-ui-strings/ja.json +++ b/assets/storefront-ui-strings/ja.json @@ -1,4 +1,8 @@ { + "layout.search.style": "検索スタイル", + "layout.search.full_width": "全幅バー", + "layout.search.expandable": "アイコン → 展開", + "layout.search.overlay": "全画面オーバーレイ", "cart.title": "カート", "cart.aria": "ショッピングカート", "cart.close": "カートを閉じる", @@ -682,6 +686,9 @@ "navigation.back_to_top": "ページ上部へ", "nav.shop": "ショップ", "nav.blog": "ブログ", + "inquiry.heading": "商品に関するお問い合わせ", + "inquiry.button": "この商品について問い合わせる", + "inquiry.copy": "メッセージをお送りください。在庫状況と価格についてご連絡いたします。", "inquiry.error": "お問い合わせを送信できませんでした。もう一度お試しください。", "inquiry.success": "お問い合わせを送信しました。まもなくメールでご連絡します。", "inquiry.field.name": "お名前", diff --git a/assets/storefront-ui-strings/pl.json b/assets/storefront-ui-strings/pl.json index f48c4db..0e0b8a2 100644 --- a/assets/storefront-ui-strings/pl.json +++ b/assets/storefront-ui-strings/pl.json @@ -1,4 +1,8 @@ { + "layout.search.style": "Styl wyszukiwania", + "layout.search.full_width": "Pasek pełnej szerokości", + "layout.search.expandable": "Ikona → rozwijane pole", + "layout.search.overlay": "Nakładka pełnoekranowa", "cart.title": "Twój koszyk", "cart.aria": "Koszyk zakupowy", "cart.close": "Zamknij koszyk", @@ -682,6 +686,9 @@ "navigation.back_to_top": "Wróć na górę", "nav.shop": "Sklep", "nav.blog": "Blog", + "inquiry.heading": "Zapytanie o produkt", + "inquiry.button": "Zapytaj o ten produkt", + "inquiry.copy": "Wyślij nam wiadomość, a skontaktujemy się z Tobą w sprawie dostępności i ceny.", "inquiry.error": "Nie udało się wysłać zapytania. Spróbuj ponownie.", "inquiry.success": "Zapytanie zostało wysłane. Wkrótce odpowiemy e-mailem.", "inquiry.field.name": "Imię i nazwisko", diff --git a/functions.php b/functions.php index 6b5b3d9..f089f49 100644 --- a/functions.php +++ b/functions.php @@ -9,7 +9,7 @@ exit; } -define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.20' ); +define( 'FUNKYCOMMERCE_HEADLESS_VERSION', '1.2.21' ); /** * Whether Superfunky Pro is active and licensed. @@ -132,6 +132,7 @@ function funkycommerce_frontend_url( $path = '' ) { } require_once get_template_directory() . '/inc/headless-login.php'; +require_once get_template_directory() . '/inc/protected-content.php'; require_once get_template_directory() . '/inc/notifications.php'; require_once get_template_directory() . '/inc/checkout-context.php'; require_once get_template_directory() . '/inc/community.php'; @@ -849,6 +850,20 @@ function funkycommerce_apply_content_shortcode_aliases( $attributes, $tag ) { 'pill' => 'kickers', ); } + if ( in_array( $tag, array( 'slider', 'carousel', 'grid' ), true ) ) { + $aliases = array_merge( + array( + 'content_type' => 'type', + 'post_type' => 'type', + 'categories' => 'category', + 'product_category' => 'category', + 'post_category' => 'category', + 'tags' => 'tag', + 'authors' => 'author', + ), + $aliases + ); + } foreach ( $aliases as $alias => $canonical ) { if ( isset( $attributes[ $alias ] ) && '' !== trim( (string) $attributes[ $alias ] ) ) { $attributes[ $canonical ] = $attributes[ $alias ]; diff --git a/inc/control-center-schema.php b/inc/control-center-schema.php index 6b14b10..f673de5 100644 --- a/inc/control-center-schema.php +++ b/inc/control-center-schema.php @@ -272,7 +272,10 @@ function funkycommerce_layout_control_fields() { 'layout_show_announcement_bar' => $toggle( __( 'Show announcement bar', 'funkycommerce-headless' ), 'yes', 'show_announcement_bar' ), 'layout_announcement_scroll_effect' => $toggle( __( 'Collapse announcement on scroll', 'funkycommerce-headless' ), 'yes', 'announcement_bar_scroll_effect' ), '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_search_variant' => $select( __( 'Header search layout', 'funkycommerce-headless' ), 'full-width', array( 'full-width', 'expandable', 'overlay' ), 'header_search_variant' ), + 'layout_mobile_menu_width' => $select( __( 'Mobile menu width', 'funkycommerce-headless' ), 'standard', array( 'standard', 'wide', 'full' ), 'mobile_menu_width' ), + 'layout_mobile_menu_height' => $select( __( 'Mobile menu height', 'funkycommerce-headless' ), 'full', array( 'full', 'content' ), 'mobile_menu_height' ), + 'layout_show_code_controls' => $toggle( __( 'Show code block controls', 'funkycommerce-headless' ), 'yes', 'show_code_controls' ), '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', 'island' ), 'header_arrangement' ), 'layout_cart_trigger_variant' => $select( __( 'Header cart behavior', 'funkycommerce-headless' ), 'drawer', array( 'drawer', 'dropdown' ), 'cart_trigger_variant' ), @@ -340,6 +343,7 @@ function funkycommerce_layout_control_fields() { 'layout_cart' => $select( __( 'Cart layout', 'funkycommerce-headless' ), 'classic', array( 'classic', 'editorial' ), 'cart_layout' ), 'layout_cart_summary' => $select( __( 'Cart summary position', 'funkycommerce-headless' ), 'sticky', array( 'sticky', 'static' ), 'cart_summary_position' ), 'layout_product_archive_hero' => $select( __( 'Product archive hero', 'funkycommerce-headless' ), 'split', array( 'split', 'fullbleed', 'minimal' ), 'product_archive_hero_layout' ), + 'layout_show_product_archive_subcategories' => $toggle( __( 'Show subcategories on product category archives', 'funkycommerce-headless' ), 'yes', 'show_product_archive_subcategories' ), 'layout_post_archive_hero' => $select( __( 'Journal archive hero', 'funkycommerce-headless' ), 'split', array( 'split', 'fullbleed', 'minimal' ), 'post_archive_hero_layout' ), 'layout_archive_description_hero' => $toggle( __( 'Show taxonomy description excerpt in archive hero', 'funkycommerce-headless' ), 'no', 'show_archive_description_in_hero' ), 'layout_post_toc' => $select( __( 'Journal post table of contents', 'funkycommerce-headless' ), 'current', array( 'current', 'rail-left', 'rail-right', 'above' ), 'post_toc_layout' ), diff --git a/inc/native-shortcodes.php b/inc/native-shortcodes.php index 577ffc6..dc3f953 100644 --- a/inc/native-shortcodes.php +++ b/inc/native-shortcodes.php @@ -217,6 +217,37 @@ function funkycommerce_native_csv_ints( $value ) { return $ids; } +/** + * Resolve comma-separated user IDs, logins, or nicenames to user IDs. + * + * A supplied list containing no valid users deliberately resolves to an empty + * result, rather than accidentally removing the author constraint. + * + * @param string $value Raw author filter. + * @return int[] + */ +function funkycommerce_native_author_ids( $value ) { + $ids = array(); + foreach ( preg_split( '/[\s,|]+/', trim( (string) $value ) ) as $candidate ) { + if ( '' === $candidate ) { + continue; + } + if ( ctype_digit( $candidate ) && (int) $candidate > 0 ) { + $user = get_user_by( 'id', (int) $candidate ); + } else { + $slug = sanitize_title( $candidate ); + $user = $slug ? get_user_by( 'slug', $slug ) : false; + if ( ! $user && $slug ) { + $user = get_user_by( 'login', $slug ); + } + } + if ( $user ) { + $ids[] = (int) $user->ID; + } + } + return array_values( array_unique( $ids ) ); +} + /** * Parse a comma-separated list of taxonomy term slugs. * @@ -438,7 +469,7 @@ function funkycommerce_native_query_products( $filters, $args_overrides = array( } if ( ! empty( $filters['author'] ) ) { - $author_ids = funkycommerce_native_csv_ints( $filters['author'] ); + $author_ids = funkycommerce_native_author_ids( $filters['author'] ); if ( ! $author_ids ) { return $empty; } @@ -558,7 +589,7 @@ function funkycommerce_native_query_posts( $post_type, $filters, $args_overrides $args['tax_query'] = $tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query } - $author_ids = ! empty( $filters['author'] ) ? funkycommerce_native_csv_ints( $filters['author'] ) : array(); + $author_ids = ! empty( $filters['author'] ) ? funkycommerce_native_author_ids( $filters['author'] ) : array(); if ( $is_community ) { $visible = function_exists( 'funkycommerce_visible_community_user_ids' ) ? funkycommerce_visible_community_user_ids() : array(); $scoped = $author_ids ? array_values( array_intersect( $author_ids, $visible ) ) : $visible; @@ -1127,7 +1158,7 @@ function funkycommerce_native_render_slider( $atts ) { $kickers = funkycommerce_native_slider_split( $a['kickers'] ); $manual_count = max( count( $titles ), count( $descriptions ), count( $images ), count( $kickers ) ); - $is_manual = $manual_count > 0 || '' !== trim( (string) $a['title'] ); + $is_manual = in_array( $a['type'], array( 'campaign', 'cinematic' ), true ) || $manual_count > 0; $slides_html = ''; $count = 0; @@ -1167,7 +1198,7 @@ function funkycommerce_native_render_slider( $atts ) { } else { $type = in_array( $a['type'], array( 'product', 'post' ), true ) ? $a['type'] : 'product'; $filters = $a; - $filters['limit'] = min( max( 1, (int) $a['slides'] ), max( 1, (int) $a['limit'] ) ); + $filters['limit'] = max( 1, (int) $a['limit'] ); $result = funkycommerce_native_query_by_type( $type, $filters ); foreach ( $result['ids'] as $id ) { $card = funkycommerce_native_card_for_type( $type, $id, $a['card_variant'] ); diff --git a/inc/protected-content.php b/inc/protected-content.php new file mode 100644 index 0000000..4ed65a2 --- /dev/null +++ b/inc/protected-content.php @@ -0,0 +1,237 @@ +header( 'Cache-Control', 'private, no-store, no-cache, must-revalidate, max-age=0' ); + $response->header( 'Pragma', 'no-cache' ); + $response->header( 'Vary', 'Authorization, X-WPGraphQL-Login-Token, X-FunkyCommerce-Page-Proof' ); + } + return $response; +} + +function funkycommerce_protected_page_from_request( WP_REST_Request $request ) { + $uri = '/' . ltrim( (string) $request->get_param( 'uri' ), '/' ); + $path = trim( (string) wp_parse_url( $uri, PHP_URL_PATH ), '/' ); + $page = get_page_by_path( $path, OBJECT, 'page' ); + return $page instanceof WP_Post ? $page : null; +} + +function funkycommerce_protected_page_proof( WP_Post $page, $expires ) { + $payload = $page->ID . '|' . $expires . '|' . $page->post_modified_gmt . '|' . $page->post_password; + return $expires . '.' . hash_hmac( 'sha256', $payload, wp_salt( 'auth' ) ); +} + +function funkycommerce_verify_protected_page_proof( WP_Post $page, $proof ) { + if ( ! preg_match( '/^(\d{10})\.([a-f0-9]{64})$/', (string) $proof, $matches ) ) { + return false; + } + $expires = (int) $matches[1]; + return $expires >= time() && hash_equals( funkycommerce_protected_page_proof( $page, $expires ), (string) $proof ); +} + +function funkycommerce_verify_native_post_password( WP_Post $page, $password ) { + require_once ABSPATH . WPINC . '/class-phpass.php'; + $cookie_name = 'wp-postpass_' . COOKIEHASH; + $previous = $_COOKIE[ $cookie_name ] ?? null; + $hasher = new PasswordHash( 8, true ); + $_COOKIE[ $cookie_name ] = $hasher->HashPassword( (string) $password ); + $required = post_password_required( $page ); + if ( null === $previous ) { + unset( $_COOKIE[ $cookie_name ] ); + } else { + $_COOKIE[ $cookie_name ] = $previous; + } + return ! $required; +} + +function funkycommerce_protected_page_response( WP_Post $page ) { + $response = rest_ensure_response( + array( + 'id' => (string) $page->ID, + 'databaseId' => (int) $page->ID, + 'slug' => $page->post_name, + 'uri' => (string) wp_make_link_relative( get_permalink( $page ) ), + 'title' => get_the_title( $page ), + 'content' => apply_filters( 'the_content', $page->post_content ), + 'modified' => get_post_modified_time( DATE_W3C, true, $page ), + 'passwordProtected' => '' !== $page->post_password, + ) + ); + return funkycommerce_protected_content_no_store( $response ); +} + +function funkycommerce_protected_unlock_rate_key( WP_Post $page ) { + $address = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? 'unknown' ) ); + return 'fc_page_unlock_' . hash_hmac( 'sha256', $address . '|' . $page->ID, wp_salt( 'nonce' ) ); +} + +function funkycommerce_protected_unlock_rate_check( WP_Post $page ) { + $attempts = array_values( + array_filter( + (array) get_transient( funkycommerce_protected_unlock_rate_key( $page ) ), + static fn( $attempted_at ) => (int) $attempted_at > time() - FUNKYCOMMERCE_PROTECTED_UNLOCK_WINDOW + ) + ); + if ( count( $attempts ) < FUNKYCOMMERCE_PROTECTED_UNLOCK_LIMIT ) { + return true; + } + $retry_after = max( 1, FUNKYCOMMERCE_PROTECTED_UNLOCK_WINDOW - ( time() - (int) min( $attempts ) ) ); + return new WP_Error( + 'funkycommerce_page_unlock_rate_limited', + __( 'Too many password attempts. Please wait before trying again.', 'funkycommerce-headless' ), + array( 'status' => 429, 'retry_after' => $retry_after ) + ); +} + +function funkycommerce_protected_unlock_record_failure( WP_Post $page ) { + $key = funkycommerce_protected_unlock_rate_key( $page ); + $attempts = (array) get_transient( $key ); + $attempts[] = time(); + set_transient( $key, array_slice( $attempts, -FUNKYCOMMERCE_PROTECTED_UNLOCK_LIMIT ), FUNKYCOMMERCE_PROTECTED_UNLOCK_WINDOW ); +} + +function funkycommerce_get_protected_page( WP_REST_Request $request ) { + funkycommerce_graphql_login_user_id(); + $page = funkycommerce_protected_page_from_request( $request ); + if ( ! $page || ( 'private' !== $page->post_status && '' === $page->post_password ) ) { + return new WP_Error( 'funkycommerce_protected_not_found', __( 'Protected page not found.', 'funkycommerce-headless' ), array( 'status' => 404 ) ); + } + + if ( 'private' === $page->post_status ) { + if ( ! current_user_can( 'read_private_pages' ) || ! current_user_can( 'read_post', $page->ID ) ) { + return new WP_Error( 'funkycommerce_private_page_auth_required', __( 'Sign in with permission to read this private page.', 'funkycommerce-headless' ), array( 'status' => 401 ) ); + } + return funkycommerce_protected_page_response( $page ); + } + + if ( ! funkycommerce_verify_protected_page_proof( $page, $request->get_header( 'X-FunkyCommerce-Page-Proof' ) ) ) { + return new WP_Error( 'funkycommerce_page_password_required', __( 'This page requires a password.', 'funkycommerce-headless' ), array( 'status' => 403 ) ); + } + return funkycommerce_protected_page_response( $page ); +} + +function funkycommerce_unlock_protected_page( WP_REST_Request $request ) { + $page = funkycommerce_protected_page_from_request( $request ); + if ( ! $page || '' === $page->post_password || 'publish' !== $page->post_status ) { + return new WP_Error( 'funkycommerce_protected_not_found', __( 'Protected page not found.', 'funkycommerce-headless' ), array( 'status' => 404 ) ); + } + $rate_check = funkycommerce_protected_unlock_rate_check( $page ); + if ( is_wp_error( $rate_check ) ) { + return $rate_check; + } + $password = (string) $request->get_param( 'password' ); + if ( ! funkycommerce_verify_native_post_password( $page, $password ) ) { + funkycommerce_protected_unlock_record_failure( $page ); + return new WP_Error( 'funkycommerce_page_password_invalid', __( 'The page password is incorrect.', 'funkycommerce-headless' ), array( 'status' => 403 ) ); + } + delete_transient( funkycommerce_protected_unlock_rate_key( $page ) ); + $expires = time() + HOUR_IN_SECONDS; + $response = rest_ensure_response( array( 'proof' => funkycommerce_protected_page_proof( $page, $expires ) ) ); + return funkycommerce_protected_content_no_store( $response ); +} + +function funkycommerce_register_protected_content_routes() { + register_rest_route( + 'funkycommerce/v1', + '/protected-page', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'funkycommerce_get_protected_page', + 'permission_callback' => '__return_true', + 'args' => array( 'uri' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ) ), + ), + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'funkycommerce_unlock_protected_page', + 'permission_callback' => '__return_true', + 'args' => array( + 'uri' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ), + 'password' => array( 'required' => true, 'type' => 'string' ), + ), + ), + ) + ); +} +add_action( 'rest_api_init', 'funkycommerce_register_protected_content_routes' ); + +function funkycommerce_protected_content_rest_headers( $response, $server, $request ) { + unset( $server ); + if ( '/funkycommerce/v1/protected-page' !== $request->get_route() ) { + return $response; + } + $response = funkycommerce_protected_content_no_store( $response ); + if ( 429 === $response->get_status() ) { + $data = $response->get_data(); + if ( isset( $data['data']['retry_after'] ) ) { + $response->header( 'Retry-After', (string) absint( $data['data']['retry_after'] ) ); + } + } + return $response; +} +add_filter( 'rest_post_dispatch', 'funkycommerce_protected_content_rest_headers', 10, 3 ); + +function funkycommerce_allow_protected_content_headers( $headers ) { + $headers[] = 'X-FunkyCommerce-Page-Proof'; + return array_values( array_unique( $headers ) ); +} +add_filter( 'rest_allowed_cors_headers', 'funkycommerce_allow_protected_content_headers' ); + +/** + * WPGraphQL connections are public build/search inputs. Explicitly exclude passworded + * posts for anonymous requests in addition to WPGraphQL's native status capability checks. + */ +function funkycommerce_graphql_public_content_query_args( $query_args ) { + if ( ! funkycommerce_graphql_login_user_id() ) { + $query_args['has_password'] = false; + } + return $query_args; +} +add_filter( 'graphql_post_object_connection_query_args', 'funkycommerce_graphql_public_content_query_args' ); + +function funkycommerce_filter_protected_graphql_root_result( $result, $source, $args, $context, $info, $type_name, $field_key ) { + unset( $source, $args, $context, $info ); + if ( 'RootQuery' !== $type_name || ! in_array( $field_key, array( 'page', 'post' ), true ) || ! is_object( $result ) ) { + return $result; + } + $post_id = isset( $result->databaseId ) ? absint( $result->databaseId ) : 0; + $post = $post_id ? get_post( $post_id ) : null; + if ( ! $post instanceof WP_Post ) { + return $result; + } + if ( '' !== $post->post_password ) { + return null; + } + if ( 'private' === $post->post_status && ( ! funkycommerce_graphql_login_user_id() || ! current_user_can( 'read_post', $post_id ) ) ) { + return null; + } + return $result; +} +add_filter( 'graphql_resolve_field', 'funkycommerce_filter_protected_graphql_root_result', 10, 7 ); + +function funkycommerce_exclude_protected_content_from_public_queries( WP_Query $query ) { + if ( ! is_admin() && ( $query->is_search() || $query->is_feed() ) ) { + $query->set( 'has_password', false ); + $query->set( 'post_status', 'publish' ); + } +} +add_action( 'pre_get_posts', 'funkycommerce_exclude_protected_content_from_public_queries' ); + +function funkycommerce_exclude_protected_content_from_sitemaps( $args ) { + $args['has_password'] = false; + $args['post_status'] = 'publish'; + return $args; +} +add_filter( 'wp_sitemaps_posts_query_args', 'funkycommerce_exclude_protected_content_from_sitemaps' ); diff --git a/inc/submissions.php b/inc/submissions.php index 9deca4d..32b57b7 100644 --- a/inc/submissions.php +++ b/inc/submissions.php @@ -511,6 +511,9 @@ function funkycommerce_store_submission_files( WP_REST_Request $request, $settin if ( ! $files ) { return array(); } + if ( ! funkycommerce_is_pro() ) { + return new WP_Error( 'funkycommerce_uploads_require_pro', __( 'File uploads require Superfunky Pro.', 'funkycommerce-headless' ), array( 'status' => 403 ) ); + } if ( 'yes' !== ( $settings['forms_upload_enabled'] ?? 'no' ) ) { return new WP_Error( 'funkycommerce_uploads_disabled', __( 'File uploads are not enabled for this form.', 'funkycommerce-headless' ), array( 'status' => 400 ) ); } @@ -589,9 +592,6 @@ function funkycommerce_delete_stored_submission_files( $files ) { */ function funkycommerce_rest_create_form_submission( WP_REST_Request $request ) { $settings = function_exists( 'funkycommerce_control_center_settings' ) ? funkycommerce_control_center_settings() : array(); - if ( ! funkycommerce_is_pro() ) { - return new WP_Error( 'funkycommerce_forms_disabled', __( 'Multi-input form submissions require Superfunky Pro.', 'funkycommerce-headless' ), array( 'status' => 503 ) ); - } $rate_check = funkycommerce_check_submission_rate_limit( 'form' ); if ( is_wp_error( $rate_check ) ) { @@ -915,7 +915,7 @@ function funkycommerce_render_submission_form( $attributes = array() ) { 'formId' => 'sample-contact', 'formName' => __( 'Sample contact form', 'funkycommerce-headless' ), 'title' => __( 'Contact us', 'funkycommerce-headless' ), - 'uploads' => 'yes', + 'uploads' => 'no', ), $attributes ); @@ -930,7 +930,7 @@ function funkycommerce_render_submission_form( $attributes = array() ) {

- +

@@ -993,7 +993,7 @@ function funkycommerce_register_submission_form_block() { 'formId' => array( 'type' => 'string', 'default' => 'sample-contact' ), 'formName' => array( 'type' => 'string', 'default' => __( 'Sample contact form', 'funkycommerce-headless' ) ), 'title' => array( 'type' => 'string', 'default' => __( 'Contact us', 'funkycommerce-headless' ) ), - 'uploads' => array( 'type' => 'boolean', 'default' => true ), + 'uploads' => array( 'type' => 'boolean', 'default' => false ), ), 'render_callback' => static function ( $attributes ) { $attributes['uploads'] = ! empty( $attributes['uploads'] ) ? 'yes' : 'no'; diff --git a/package-lock.json b/package-lock.json index ac386e5..7760d41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "funkycommerce-headless-theme", - "version": "1.2.20", + "version": "1.2.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "funkycommerce-headless-theme", - "version": "1.2.20", + "version": "1.2.21", "devDependencies": { "autoprefixer": "^10.4.20", "esbuild": "^0.28.2", diff --git a/package.json b/package.json index ee14d35..17c10d4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "funkycommerce-headless-theme", "private": true, - "version": "1.2.20", + "version": "1.2.21", "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 8cc30cb..31e6d1a 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.20 +Stable tag: 1.2.21 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.21 highlights == + +* Supports capability-gated private pages and password-protected content without exposing protected pages through public caches, feeds, search, sitemaps, or prerendering. +* Preserves safe storefront return paths through login, registration, and provider authentication while rejecting external redirects. +* Adds opaque storefront media aliases while keeping existing upload links compatible. +* Adds full-screen header search, configurable mobile-menu dimensions, and a global code-block controls setting. + == 1.2.20 highlights == * Recovers header controls incorrectly disabled by section-specific Control Center saves and preserves schema defaults for newly introduced fields. diff --git a/style.css b/style.css index 9c0fec2..8efe53b 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.20 +Version: 1.2.21 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