diff --git a/assets/css/admin.scss b/assets/css/admin.scss index da96a5b5..0eae8481 100644 --- a/assets/css/admin.scss +++ b/assets/css/admin.scss @@ -151,6 +151,25 @@ span.woocommerce-gzd-input-toggle { margin-right: 1em; } +.product_data .form-field-wc-gzd-wp-editor { + display: flex; + flex-wrap: wrap; + padding-bottom: 1em; + + label { + float: none; + width: 150px; + margin: 0; + padding: 12px; + padding-right: 0; + } + + .wc-gzd-product-editor-wrap { + flex-grow: 1; + padding-right: 20px; + } +} + .wc-gzd-pro, .product_tab_gzd_pro a::after, .product_type_gzd_pro::after { font-size: 9px; border-radius: 3px; diff --git a/assets/js/blocks/product-elements/component-init.js b/assets/js/blocks/product-elements/component-init.js index 8180dcb3..2291288a 100644 --- a/assets/js/blocks/product-elements/component-init.js +++ b/assets/js/blocks/product-elements/component-init.js @@ -83,4 +83,31 @@ registerBlockComponent( { /* webpackChunkName: "product-defect-description" */ './defect-description/frontend' ) ), +} ); + +registerBlockComponent( { + blockName: 'woocommerce-germanized/product-manufacturer', + component: lazy( () => + import( + /* webpackChunkName: "product-manufacturer" */ './manufacturer/frontend' + ) + ), +} ); + +registerBlockComponent( { + blockName: 'woocommerce-germanized/product-safety-attachments', + component: lazy( () => + import( + /* webpackChunkName: "product-safety-attachments" */ './product-safety-attachments/frontend' + ) + ), +} ); + +registerBlockComponent( { + blockName: 'woocommerce-germanized/product-safety-instructions', + component: lazy( () => + import( + /* webpackChunkName: "product-safety-instructions" */ './safety-instructions/frontend' + ) + ), } ); \ No newline at end of file diff --git a/assets/js/blocks/product-elements/index.js b/assets/js/blocks/product-elements/index.js index 2e01950c..25dc7421 100644 --- a/assets/js/blocks/product-elements/index.js +++ b/assets/js/blocks/product-elements/index.js @@ -9,5 +9,6 @@ import './deposit-packaging-type'; import './defect-description'; import './manufacturer'; import './product-safety-attachments'; +import './safety-instructions'; export * from './component-init' \ No newline at end of file diff --git a/assets/js/blocks/product-elements/safety-instructions/block.js b/assets/js/blocks/product-elements/safety-instructions/block.js new file mode 100644 index 00000000..9ce9e3e0 --- /dev/null +++ b/assets/js/blocks/product-elements/safety-instructions/block.js @@ -0,0 +1,19 @@ +/** + * External dependencies + */ +import { withProductDataContext } from '@woocommerce/shared-hocs'; +import PriceLabelBlock from '../shared/price-label-block'; +export default ( props ) => { + props = { ...props, 'labelType': 'safety_instructions' }; + + // It is necessary because this block has to support serveral contexts: + // - Inside `All Products Block` -> `withProductDataContext` HOC + // - Inside `Products Block` -> Gutenberg Context + // - Inside `Single Product Template` -> Gutenberg Context + // - Without any parent -> `WithSelector` and `withProductDataContext` HOCs + // For more details, check https://github.com/woocommerce/woocommerce-blocks/pull/8609 + if ( props.isDescendentOfSingleProductTemplate ) { + return ; + } + return withProductDataContext( PriceLabelBlock )( props ); +}; diff --git a/assets/js/blocks/product-elements/safety-instructions/edit.js b/assets/js/blocks/product-elements/safety-instructions/edit.js new file mode 100644 index 00000000..16db7340 --- /dev/null +++ b/assets/js/blocks/product-elements/safety-instructions/edit.js @@ -0,0 +1,65 @@ +/** + * External dependencies + */ +import { + AlignmentToolbar, + BlockControls, + useBlockProps, +} from '@wordpress/block-editor'; +import { useEffect } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import Block from './block'; +import { useIsDescendentOfSingleProductTemplate } from '../shared/use-is-descendent-of-single-product-template'; +const Edit = ( { + attributes, + setAttributes, + context, +} ) => { + const blockProps = useBlockProps(); + const blockAttrs = { + ...attributes, + ...context, + }; + const isDescendentOfQueryLoop = Number.isFinite( context.queryId ); + + let { isDescendentOfSingleProductTemplate } = + useIsDescendentOfSingleProductTemplate( { isDescendentOfQueryLoop } ); + + if ( isDescendentOfQueryLoop ) { + isDescendentOfSingleProductTemplate = false; + } + + useEffect( + () => + setAttributes( { + isDescendentOfQueryLoop, + isDescendentOfSingleProductTemplate, + } ), + [ + isDescendentOfQueryLoop, + isDescendentOfSingleProductTemplate, + setAttributes, + ] + ); + + return ( + <> + + { + setAttributes( { textAlign } ); + } } + /> + +
+ +
+ + ); +}; + +export default Edit; \ No newline at end of file diff --git a/assets/js/blocks/product-elements/safety-instructions/frontend.js b/assets/js/blocks/product-elements/safety-instructions/frontend.js new file mode 100644 index 00000000..60ff2a81 --- /dev/null +++ b/assets/js/blocks/product-elements/safety-instructions/frontend.js @@ -0,0 +1,5 @@ +/** + * Internal dependencies + */ +import Block from './block'; +export default Block; diff --git a/assets/js/blocks/product-elements/safety-instructions/index.js b/assets/js/blocks/product-elements/safety-instructions/index.js new file mode 100644 index 00000000..a9f04802 --- /dev/null +++ b/assets/js/blocks/product-elements/safety-instructions/index.js @@ -0,0 +1,38 @@ +/** + * External dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; +import { page, Icon } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import { getSetting } from '@germanized/settings'; + +/** + * Internal dependencies + */ +import sharedConfig from '../shared/config'; +import edit from './edit'; + +const { ancestor, ...configuration } = sharedConfig; + +const blockConfig = { + ...configuration, + apiVersion: 2, + title: __( 'Safety instructions', 'woocommerce-germanized' ) + ( ! getSetting( 'isPro' ) ? ' (Pro)' : '' ), + description: __( 'Inserts the product\'s safety instructions.', 'woocommerce-germanized' ), + usesContext: [ 'query', 'queryId', 'postId' ], + icon: { src: }, + + supports: { + ...sharedConfig.supports, + ...( { + __experimentalSelector: + '.wp-block-woocommerce-gzd-product-safety-instructions .wc-gzd-block-components-product-safety-instructions', + } ) + }, + edit, +}; + +registerBlockType( 'woocommerce-germanized/product-safety-instructions', blockConfig ); diff --git a/assets/js/blocks/product-elements/shared/price-label-block.js b/assets/js/blocks/product-elements/shared/price-label-block.js index 38402ab0..1a1c589c 100644 --- a/assets/js/blocks/product-elements/shared/price-label-block.js +++ b/assets/js/blocks/product-elements/shared/price-label-block.js @@ -38,6 +38,7 @@ const getPreviewData = ( labelType, productData, isDescendentOfSingleProductTemp 'deposit_packaging_type_html': '', 'manufacturer_html': '', 'product_safety_attachments_html': '', + 'safety_instructions_html': '', }; const prices = productData.prices; @@ -111,6 +112,14 @@ const getPreviewData = ( labelType, productData, isDescendentOfSingleProductTemp ); + } else if ( 'safety_instructions' === labelTypeData ) { + formattedPreview = ( + <> +

+ { _x( 'Sample safety instructions for a certain product.', 'preview', 'woocommerce-germanized' ) }
+

+ + ); } return { diff --git a/assets/js/static/add-to-cart-variation.js b/assets/js/static/add-to-cart-variation.js index af061e2d..4bc8aa35 100644 --- a/assets/js/static/add-to-cart-variation.js +++ b/assets/js/static/add-to-cart-variation.js @@ -135,6 +135,8 @@ form.getElementOrBlock( form, 'manufacturer-heading', '.wc-gzd-product-manufacturer-heading' ).wc_gzd_set_content( variation.manufacturer_heading ); form.getElementOrBlock( form, 'product_safety_attachments', '.product-safety-attachments' ).wc_gzd_set_content( variation.product_safety_attachments ); form.getElementOrBlock( form, 'product-safety-attachments-heading', '.wc-gzd-product-safety-attachments-heading' ).wc_gzd_set_content( variation.product_safety_attachments_heading ); + form.getElementOrBlock( form, 'safety_instructions', '.safety-instructions' ).wc_gzd_set_content( variation.safety_instructions ); + form.getElementOrBlock( form, 'safety-instructions-heading', '.wc-gzd-product-safety-instructions-heading' ).wc_gzd_set_content( variation.safety_instructions_heading ); form.getElementOrBlock( form, 'deposit', '.deposit-amount' ).wc_gzd_set_content( hasDisplayPrice ? variation.deposit_amount : '' ); form.getElementOrBlock( form, 'deposit-packaging-type', '.deposit-packaging-type' ).wc_gzd_set_content( hasDisplayPrice ? variation.deposit_packaging_type : '' ); diff --git a/includes/abstracts/abstract-wc-gzd-product.php b/includes/abstracts/abstract-wc-gzd-product.php index ad590188..beb7bd64 100644 --- a/includes/abstracts/abstract-wc-gzd-product.php +++ b/includes/abstracts/abstract-wc-gzd-product.php @@ -333,7 +333,7 @@ public function get_manufacturer_slug( $context = 'view' ) { } public function has_product_safety_information() { - return apply_filters( 'woocommerce_gzd_product_has_safety_information', ( $this->get_safety_attachment_ids() || $this->get_manufacturer() ) ); + return apply_filters( 'woocommerce_gzd_product_has_safety_information', ( $this->get_safety_attachment_ids() || $this->get_manufacturer() || $this->get_safety_instructions() ) ); } public function set_manufacturer_slug( $slug ) { @@ -666,6 +666,21 @@ public function get_defect_description( $context = 'view' ) { return $this->get_prop( 'defect_description', $context ); } + public function get_safety_instructions( $context = 'view' ) { + return $this->get_prop( 'safety_instructions', $context ); + } + + /** + * @return string + */ + public function get_formatted_safety_instructions( $context = 'view' ) { + if ( $instructions = $this->get_safety_instructions( $context ) ) { + return wpautop( do_shortcode( wp_kses_post( htmlspecialchars_decode( $instructions ) ) ) ); + } + + return ''; + } + public function get_cart_description( $context = 'view' ) { return $this->get_mini_desc(); } @@ -908,6 +923,10 @@ public function set_food_description( $description ) { $this->set_prop( 'food_description', $description ); } + public function set_safety_instructions( $instructions ) { + $this->set_prop( 'safety_instructions', $instructions ); + } + public function set_unit_price( $price ) { $this->set_prop( 'unit_price', wc_format_decimal( $price ) ); } diff --git a/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data-variable.php b/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data-variable.php index 597d4f70..40beaabd 100644 --- a/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data-variable.php +++ b/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data-variable.php @@ -402,6 +402,11 @@ public static function output( $loop, $variation_data, $variation ) { ?>

+

+ + +

+

@@ -585,6 +590,7 @@ public static function save( $variation_id, $i ) { '_gtin' => '', '_mpn' => '', '_safety_attachment_ids' => '', + '_safety_instructions' => '', '_warranty_attachment_id' => '', '_nutrient_ids' => '', '_nutrient_reference_value' => '', diff --git a/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data.php b/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data.php index 92e2e800..810c7c0f 100644 --- a/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data.php +++ b/includes/admin/meta-boxes/class-wc-germanized-meta-box-product-data.php @@ -681,6 +681,24 @@ public static function output() { $manufacturer ) ); ?>

+
+ +
+ get_safety_instructions( 'edit' ) ), + '_safety_instructions', + array( + 'textarea_name' => '_safety_instructions', + 'textarea_rows' => 2, + 'media_buttons' => false, + 'teeny' => apply_filters( 'woocommerce_gzd_product_wp_editor_use_teeny', true ), + ) + ); + ?> +
+
+

@@ -982,6 +1000,7 @@ public static function get_fields() { '_defect_description' => '', '_warranty_attachment_id' => '', '_safety_attachment_ids' => '', + '_safety_instructions' => '', '_gtin' => '', '_mpn' => '', 'delivery_time' => '', @@ -1020,6 +1039,7 @@ protected static function get_field_sanitization_type( $field ) { '_food_place_of_origin', '_ingredients', '_defect_description', + '_safety_instructions', '_mini_desc', ); @@ -1361,6 +1381,10 @@ public static function save_product_data( &$product, $data, $is_variation = fals $gzd_product->set_defect_description( '' === $data['_defect_description'] ? '' : wc_gzd_sanitize_html_text_field( $data['_defect_description'] ) ); } + if ( isset( $data['_safety_instructions'] ) ) { + $gzd_product->set_safety_instructions( '' === $data['_safety_instructions'] ? '' : wc_gzd_sanitize_html_text_field( $data['_safety_instructions'] ) ); + } + if ( isset( $data['_nutrient_ids'] ) ) { $gzd_product->set_nutrient_ids( (array) wc_clean( $data['_nutrient_ids'] ) ); } diff --git a/includes/class-wc-gzd-product-variation.php b/includes/class-wc-gzd-product-variation.php index 348dc993..574304b9 100644 --- a/includes/class-wc-gzd-product-variation.php +++ b/includes/class-wc-gzd-product-variation.php @@ -44,6 +44,7 @@ class WC_GZD_Product_Variation extends WC_GZD_Product { 'warranty_attachment_id', 'manufacturer_slug', 'safety_attachment_ids', + 'safety_instructions', 'gtin', 'mpn', 'deposit_type', diff --git a/includes/class-wc-gzd-shortcodes.php b/includes/class-wc-gzd-shortcodes.php index 5a703621..890a9950 100644 --- a/includes/class-wc-gzd-shortcodes.php +++ b/includes/class-wc-gzd-shortcodes.php @@ -38,6 +38,7 @@ public static function init() { 'gzd_product_safety_information' => __CLASS__ . '::gzd_product_safety_information', 'gzd_product_manufacturer' => __CLASS__ . '::gzd_product_manufacturer', 'gzd_product_safety_attachments' => __CLASS__ . '::gzd_product_safety_attachments', + 'gzd_product_safety_instructions' => __CLASS__ . '::gzd_product_safety_instructions', 'gzd_product_deposit' => __CLASS__ . '::gzd_product_deposit', 'gzd_product_deposit_packaging_type' => __CLASS__ . '::gzd_product_deposit_packaging_type', 'gzd_email_legal_page_attachments' => __CLASS__ . '::gzd_email_legal_page_attachments', @@ -181,6 +182,18 @@ public static function gzd_product_safety_attachments( $atts ) { return apply_filters( 'woocommerce_gzd_shortcode_product_safety_attachments_html', self::get_gzd_product_shortcode( $atts, 'woocommerce_gzd_template_single_product_safety_attachments' ), $atts ); } + public static function gzd_product_safety_instructions( $atts ) { + /** + * Filter shortcode product safety instructions output. + * + * @param string $html The output. + * @param array $atts The shortcode arguments. + * + * @since 3.18.0 + */ + return apply_filters( 'woocommerce_gzd_shortcode_product_safety_instructions_html', self::get_gzd_product_shortcode( $atts, 'woocommerce_gzd_template_single_safety_instructions' ), $atts ); + } + public static function gzd_product_manufacturer( $atts ) { /** * Filter shortcode product manufacturer output. diff --git a/includes/export/class-wc-gzd-product-export.php b/includes/export/class-wc-gzd-product-export.php index a7700146..cd5c2523 100644 --- a/includes/export/class-wc-gzd-product-export.php +++ b/includes/export/class-wc-gzd-product-export.php @@ -91,6 +91,7 @@ public function get_columns() { 'unit' => _x( 'Unit', 'exporter', 'woocommerce-germanized' ), 'manufacturer' => _x( 'Manufacturer', 'exporter', 'woocommerce-germanized' ), 'safety_attachment_ids' => _x( 'Safety attachments ids', 'exporter', 'woocommerce-germanized' ), + 'safety_instructions' => _x( 'Safety instructions', 'exporter', 'woocommerce-germanized' ), 'unit_base' => _x( 'Unit base', 'exporter', 'woocommerce-germanized' ), 'unit_product' => _x( 'Unit product', 'exporter', 'woocommerce-germanized' ), 'mini_desc' => _x( 'Cart description', 'exporter', 'woocommerce-germanized' ), @@ -242,7 +243,7 @@ public function export_column( $value, $product ) { $filter = current_filter(); $column_name = str_replace( 'woocommerce_product_export_product_column_', '', $filter ); $gzd_product = wc_gzd_get_product( $product ); - $is_html_field = in_array( $column_name, array( 'ingredients', 'food_description', 'food_place_of_origin', 'food_distributor', 'defect_description', 'mini_desc' ), true ); + $is_html_field = in_array( $column_name, array( 'ingredients', 'food_description', 'food_place_of_origin', 'food_distributor', 'defect_description', 'mini_desc', 'safety_instructions' ), true ); /** * Delivery time needs special handling diff --git a/includes/import/class-wc-gzd-product-import.php b/includes/import/class-wc-gzd-product-import.php index f049cb96..a1a1044a 100644 --- a/includes/import/class-wc-gzd-product-import.php +++ b/includes/import/class-wc-gzd-product-import.php @@ -71,6 +71,7 @@ public function get_formatting_callbacks() { array( 'mini_desc' => array( $this, 'parse_html_field' ), 'defect_description' => array( $this, 'parse_html_field' ), + 'safety_instructions' => array( $this, 'parse_html_field' ), 'unit_price_regular' => 'wc_format_decimal', 'unit_price_sale' => 'wc_format_decimal', 'unit_base' => 'wc_format_decimal', diff --git a/includes/wc-gzd-template-functions.php b/includes/wc-gzd-template-functions.php index 560f7121..e4ec435b 100644 --- a/includes/wc-gzd-template-functions.php +++ b/includes/wc-gzd-template-functions.php @@ -129,6 +129,20 @@ function woocommerce_gzd_template_single_manufacturer( $args = array() ) { } } +if ( ! function_exists( 'woocommerce_gzd_template_single_safety_instructions' ) ) { + + function woocommerce_gzd_template_single_safety_instructions( $args = array() ) { + $args = wp_parse_args( + $args, + array( + 'print_title' => true, + ) + ); + + wc_get_template( 'single-product/safety-instructions.php', $args ); + } +} + if ( ! function_exists( 'woocommerce_gzd_template_product_rating_authenticity_status_loop' ) ) { function woocommerce_gzd_template_product_rating_authenticity_status_loop() { global $product; @@ -815,6 +829,7 @@ function woocommerce_gzd_add_variation_options( $options, $product, $variation ) 'product_safety_attachments' => $gzd_product->get_product_safety_attachments_html(), 'has_product_safety_information' => $gzd_product->has_product_safety_information(), 'manufacturer' => $gzd_product->get_manufacturer_html(), + 'safety_instructions' => $gzd_product->get_formatted_safety_instructions(), 'is_food' => $gzd_product->is_food() ? 'yes' : 'no', 'food_description' => $gzd_product->is_food() ? $gzd_product->get_formatted_food_description() : '', 'food_place_of_origin' => $gzd_product->is_food() ? $gzd_product->get_formatted_food_place_of_origin() : '', @@ -849,6 +864,10 @@ function woocommerce_gzd_add_variation_options( $options, $product, $variation ) if ( ! empty( $options['product_safety_attachments'] ) ) { $options['product_safety_attachments_heading'] = esc_html( apply_filters( 'woocommerce_gzd_product_safety_attachments_heading', __( 'Product safety documents', 'woocommerce-germanized' ) ) ); } + + if ( $gzd_product->get_safety_instructions() ) { + $options['safety_instructions_heading'] = esc_html( apply_filters( 'woocommerce_gzd_product_safety_instructions_heading', __( 'Safety instructions', 'woocommerce-germanized' ) ) ); + } } return $options; diff --git a/src/Blocks/BlockTypes/ProductSafetyInstructions.php b/src/Blocks/BlockTypes/ProductSafetyInstructions.php new file mode 100644 index 00000000..11ba6449 --- /dev/null +++ b/src/Blocks/BlockTypes/ProductSafetyInstructions.php @@ -0,0 +1,29 @@ +get_formatted_safety_instructions(); + } +} diff --git a/src/Blocks/BlockTypesController.php b/src/Blocks/BlockTypesController.php index 36b84b2a..3feb57f0 100644 --- a/src/Blocks/BlockTypesController.php +++ b/src/Blocks/BlockTypesController.php @@ -53,6 +53,7 @@ protected function get_block_types() { 'ProductDefectDescription', 'ProductManufacturer', 'ProductSafetyAttachments', + 'ProductSafetyInstructions', ); if ( \Vendidero\Germanized\Package::is_pro() ) { diff --git a/src/Blocks/Products.php b/src/Blocks/Products.php index a68b3b74..f459cba9 100644 --- a/src/Blocks/Products.php +++ b/src/Blocks/Products.php @@ -227,6 +227,7 @@ private function register_endpoint_data() { 'delivery_time_html' => $html_formatter->format( $gzd_product->get_delivery_time_html() ), 'manufacturer_html' => $html_formatter->format( $gzd_product->get_manufacturer_html() ), 'product_safety_attachments_html' => $html_formatter->format( $gzd_product->get_product_safety_attachments_html() ), + 'safety_instructions_html' => $html_formatter->format( $gzd_product->get_formatted_safety_instructions() ), 'tax_info_html' => $html_formatter->format( $gzd_product->get_tax_info() ), 'shipping_costs_info_html' => $html_formatter->format( $gzd_product->get_shipping_costs_html() ), 'defect_description_html' => $html_formatter->format( $gzd_product->get_formatted_defect_description() ), @@ -369,6 +370,12 @@ private function register_endpoint_data() { 'context' => array( 'view', 'edit' ), 'readonly' => true, ), + 'safety_instructions_html' => array( + 'description' => __( 'Safety instructions formatted as HTML.', 'woocommerce-germanized' ), + 'type' => 'string', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), 'nutri_score' => array( 'description' => __( 'Nutri score.', 'woocommerce-germanized' ), 'type' => 'string', diff --git a/src/Shopmarks.php b/src/Shopmarks.php index 50c052a0..c3011c88 100644 --- a/src/Shopmarks.php +++ b/src/Shopmarks.php @@ -59,6 +59,11 @@ protected static function register_single_product() { 'default_priority' => 11, 'callback' => 'woocommerce_gzd_template_single_product_safety_attachments', ), + 'safety_instructions' => array( + 'default_filter' => 'woocommerce_gzd_single_product_safety_information', + 'default_priority' => 12, + 'callback' => 'woocommerce_gzd_template_single_safety_instructions', + ), 'defect_description' => array( 'default_filter' => 'woocommerce_single_product_summary', 'default_priority' => 21, @@ -796,6 +801,7 @@ public static function get_types( $location = 'single_product' ) { 'nutri_score' => _x( 'Nutri-Score', 'shopmark', 'woocommerce-germanized' ), 'manufacturer' => _x( 'Manufacturer', 'shopmark', 'woocommerce-germanized' ), 'product_safety_attachments' => _x( 'Product safety attachments', 'shopmark', 'woocommerce-germanized' ), + 'safety_instructions' => _x( 'Safety instructions', 'shopmark', 'woocommerce-germanized' ), ), 'single_product_grouped' => array( 'unit_price' => _x( 'Unit Price', 'shopmark', 'woocommerce-germanized' ), diff --git a/templates/single-product/product-safety-attachments.php b/templates/single-product/product-safety-attachments.php index 115a093f..5b9683b8 100644 --- a/templates/single-product/product-safety-attachments.php +++ b/templates/single-product/product-safety-attachments.php @@ -12,25 +12,26 @@ * * @see https://github.com/vendidero/woocommerce-germanized/wiki/Overriding-Germanized-Templates * @package Germanized/Templates - * @version 3.17.5 + * @version 3.18.5 */ if ( ! defined( 'ABSPATH' ) ) { exit; } // Exit if accessed directly global $product; +$heading = apply_filters( 'woocommerce_gzd_product_product_safety_attachments_heading', __( 'Product safety documents', 'woocommerce-germanized' ) ); ?> get_product_safety_attachments_html() ) : ?> - -

+ +

get_product_safety_attachments_html() ); ?>
is_type( 'variable' ) ) : ?> - +

diff --git a/templates/single-product/product-safety.php b/templates/single-product/product-safety.php index d11c30f5..f94dc7f3 100644 --- a/templates/single-product/product-safety.php +++ b/templates/single-product/product-safety.php @@ -12,24 +12,25 @@ * * @see https://github.com/vendidero/woocommerce-germanized/wiki/Overriding-Germanized-Templates * @package Germanized/Templates - * @version 3.18.0 + * @version 3.18.5 */ if ( ! defined( 'ABSPATH' ) ) { exit; } // Exit if accessed directly global $product; +$heading = apply_filters( 'woocommerce_gzd_product_safety_heading', __( 'Product safety', 'woocommerce-germanized' ) ); ?> has_product_safety_information() ) : ?> - -

+ +

is_type( 'variable' ) ) : ?> - -

+ +

diff --git a/templates/single-product/safety-instructions.php b/templates/single-product/safety-instructions.php new file mode 100644 index 00000000..be94f528 --- /dev/null +++ b/templates/single-product/safety-instructions.php @@ -0,0 +1,39 @@ + + +get_formatted_safety_instructions() ) : ?> + +

+ + +
+ get_formatted_safety_instructions() ); ?> +
+is_type( 'variable' ) ) : ?> + +

+ + +
+