{"id":396943,"date":"2026-11-02T10:17:02","date_gmt":"2026-11-02T10:17:02","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/coder-vibe-gpsr-product-safety\/"},"modified":"2026-11-02T11:13:36","modified_gmt":"2026-11-02T11:13:36","slug":"coder-vibe-gpsr-product-safety","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/coder-vibe-gpsr-product-safety\/","author":23593371,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.2","stable_tag":"1.0.2","tested":"7.1.4","requires":"6.3","requires_php":"7.4","requires_plugins":null,"header_name":"Coder Vibe GPSR Product Safety","header_author":"Coder Vibe","header_description":"EU GPSR product safety information for WooCommerce: reusable manufacturer and responsible person profiles, inheritance by shop, category and brand, a missing-data report and CSV import\/export.","assets_banners_color":"508083","last_updated":"2026-11-02 11:13:36","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/codervibe.co.uk","rating":0,"author_block_rating":0,"active_installs":0,"downloads":141,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.1":{"tag":"1.0.1","author":"oozye2","date":"2026-11-02 10:16:47","revision":3779506},"1.0.2":{"tag":"1.0.2","author":"oozye2","date":"2026-11-02 11:13:36","revision":3779637}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3779507,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3779507,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3779507,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3779507,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3779637,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"meta\":{\"title\":\"Coder Vibe GPSR Product Safety demo\",\"description\":\"A WooCommerce shop with manufacturer and EU responsible person profiles assigned by category, and a couple of gaps for the GPSR check to find.\",\"author\":\"oozye2\"},\"landingPage\":\"\\\/wp-admin\\\/edit.php?post_type=product&page=cvgpsr-check\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"features\":{\"networking\":true},\"login\":true,\"steps\":[{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"woocommerce\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"coder-vibe-gpsr-product-safety\"},\"options\":{\"activate\":true}},{\"step\":\"setSiteOptions\",\"options\":{\"blogname\":\"Coder Vibe Demo Shop\",\"woocommerce_default_country\":\"GB\",\"woocommerce_currency\":\"GBP\",\"woocommerce_coming_soon\":\"no\",\"woocommerce_task_list_hidden\":\"yes\",\"woocommerce_show_marketplace_suggestions\":\"no\",\"woocommerce_onboarding_profile\":{\"skipped\":true},\"cvgpsr_display\":\"tab\"}},{\"step\":\"runPHP\",\"code\":\"<?php\\nrequire '\\\/wordpress\\\/wp-load.php';\\nrequire_once ABSPATH . 'wp-admin\\\/includes\\\/image.php';\\nwp_set_current_user( 1 );\\n\\n\\\/\\\/ Safety profiles (fictional companies; .example addresses are reserved for demos).\\nfunction cvgpsr_demo_profile( $name, $f ) {\\n\\t$id = wp_insert_post( array( 'post_type' => 'cvgpsr_party', 'post_status' => 'publish', 'post_title' => $name ) );\\n\\tforeach ( $f as $k => $v ) { update_post_meta( $id, '_cvgpsr_' . $k, $v ); }\\n\\treturn $id;\\n}\\n$candles = cvgpsr_demo_profile( 'Harbour Light Candles Ltd', array( 'trade_name' => 'Harbour Light', 'address' => \\\"Unit 4, Marine Parade\\\\nDover\\\\nCT16 1LZ\\\", 'country' => 'GB', 'email' => 'safety@harbourlight.example', 'url' => 'https:\\\/\\\/harbourlight.example', 'phone' => '+44 1304 000000' ) );\\n$rp      = cvgpsr_demo_profile( 'EuroSafe Compliance B.V.', array( 'trade_name' => 'EuroSafe', 'address' => \\\"Havenstraat 12\\\\n3011 AB Rotterdam\\\", 'country' => 'NL', 'email' => 'gpsr@eurosafe.example', 'url' => 'https:\\\/\\\/eurosafe.example', 'phone' => '+31 10 000 0000' ) );\\n$toys    = cvgpsr_demo_profile( 'Brightworks Toys GmbH', array( 'trade_name' => 'Brightworks', 'address' => \\\"Spielwarenweg 7\\\\n90402 N\\u00fcrnberg\\\", 'country' => 'DE', 'email' => 'service@brightworks.example', 'url' => 'https:\\\/\\\/brightworks.example', 'phone' => '' ) );\\n$textile = cvgpsr_demo_profile( 'Maple & Thread Ltd', array( 'trade_name' => 'Maple & Thread', 'address' => \\\"18 Mill Lane\\\\nHebden Bridge\\\", 'country' => 'GB', 'email' => '', 'url' => '', 'phone' => '' ) );\\n\\n\\\/\\\/ Categories carry the profiles, so products inherit them.\\nfunction cvgpsr_demo_cat( $name, $m, $r, $w ) {\\n\\t$t = wp_insert_term( $name, 'product_cat' );\\n\\t$id = is_wp_error( $t ) ? (int) $t->get_error_data( 'term_exists' ) : (int) $t['term_id'];\\n\\tif ( $m ) { update_term_meta( $id, '_cvgpsr_manufacturer', $m ); }\\n\\tif ( $r ) { update_term_meta( $id, '_cvgpsr_rp', $r ); }\\n\\tif ( $w ) { update_term_meta( $id, '_cvgpsr_warnings', $w ); }\\n\\treturn $id;\\n}\\n$c_candles = cvgpsr_demo_cat( 'Candles', $candles, $rp, \\\"Never leave a burning candle unattended. Burn out of reach of children and pets. Keep at least 10 cm between burning candles. Do not burn for more than 4 hours at a time.\\\" );\\n$c_toys    = cvgpsr_demo_cat( 'Toys', $toys, 0, \\\"Warning: not suitable for children under 3 years. Small parts. Choking hazard. Adult supervision recommended.\\\" );\\n$c_textile = cvgpsr_demo_cat( 'Textiles', $textile, 0, '' );\\n\\n\\\/\\\/ Simple product pictures drawn with GD (skipped if GD is unavailable).\\nfunction cvgpsr_demo_image( $slug, $kind, $rgb ) {\\n\\tif ( ! function_exists( 'imagecreatetruecolor' ) ) { return 0; }\\n\\t$im  = imagecreatetruecolor( 600, 600 );\\n\\t$col = function ( $r, $g, $b ) use ( $im ) { return imagecolorallocate( $im, $r, $g, $b ); };\\n\\t$main = $col( $rgb[0], $rgb[1], $rgb[2] );\\n\\t$dark = $col( (int) ( $rgb[0] * 0.8 ), (int) ( $rgb[1] * 0.8 ), (int) ( $rgb[2] * 0.8 ) );\\n\\tif ( 'candle' === $kind ) {\\n\\t\\timagefill( $im, 0, 0, $col( 238, 232, 222 ) );\\n\\t\\timagefilledellipse( $im, 300, 490, 270, 50, $col( 215, 207, 195 ) );\\n\\t\\timagefilledrectangle( $im, 210, 250, 390, 490, $main );\\n\\t\\timagefilledellipse( $im, 300, 250, 180, 44, $col( min( 255, $rgb[0] + 18 ), min( 255, $rgb[1] + 18 ), min( 255, $rgb[2] + 18 ) ) );\\n\\t\\timagesetthickness( $im, 5 ); imageline( $im, 300, 250, 300, 212, $col( 60, 50, 40 ) );\\n\\t\\timagefilledellipse( $im, 300, 180, 30, 66, $col( 255, 190, 60 ) ); imagefilledellipse( $im, 300, 192, 12, 34, $col( 255, 240, 180 ) );\\n\\t\\timagefilledrectangle( $im, 240, 350, 360, 410, $col( 250, 247, 240 ) ); imagefilledrectangle( $im, 255, 375, 345, 385, $dark );\\n\\t} elseif ( 'toy' === $kind ) {\\n\\t\\timagefill( $im, 0, 0, $col( 226, 238, 246 ) );\\n\\t\\timagefilledrectangle( $im, 170, 300, 430, 440, $main ); imagefilledrectangle( $im, 220, 230, 370, 305, $dark );\\n\\t\\timagefilledrectangle( $im, 238, 243, 352, 292, $col( 200, 230, 250 ) );\\n\\t\\tforeach ( array( 230, 370 ) as $x ) { imagefilledellipse( $im, $x, 450, 90, 90, $col( 40, 40, 45 ) ); imagefilledellipse( $im, $x, 450, 36, 36, $col( 180, 180, 185 ) ); }\\n\\t} elseif ( 'blocks' === $kind ) {\\n\\t\\timagefill( $im, 0, 0, $col( 246, 238, 226 ) );\\n\\t\\timagefilledrectangle( $im, 160, 360, 290, 490, $col( 220, 70, 60 ) ); imagefilledrectangle( $im, 310, 360, 440, 490, $col( 60, 130, 200 ) ); imagefilledrectangle( $im, 235, 220, 365, 350, $col( 250, 190, 40 ) );\\n\\t} else {\\n\\t\\timagefill( $im, 0, 0, $col( 236, 236, 240 ) );\\n\\t\\tfor ( $i = 0; $i < 10; $i++ ) { imagefilledrectangle( $im, 190, 120 + $i * 36, 410, 138 + $i * 36, $i % 2 ? $main : $col( 230, 215, 200 ) ); }\\n\\t}\\n\\t$up = wp_upload_dir(); $file = trailingslashit( $up['path'] ) . $slug . '.jpg';\\n\\timagejpeg( $im, $file, 88 ); imagedestroy( $im );\\n\\t$att = wp_insert_attachment( array( 'post_mime_type' => 'image\\\/jpeg', 'post_title' => $slug, 'post_status' => 'inherit' ), $file );\\n\\twp_update_attachment_metadata( $att, wp_generate_attachment_metadata( $att, $file ) );\\n\\treturn $att;\\n}\\n\\n\\\/\\\/ Products. Two are left incomplete on purpose so the GPSR check has something to find.\\n$products = array(\\n\\tarray( 'Sea Salt & Driftwood Candle', '18.00', 'HL-CAN-SEA', $c_candles, 'candle', array( 214, 226, 232 ) ),\\n\\tarray( 'Fig & Blackcurrant Candle', '18.00', 'HL-CAN-FIG', $c_candles, 'candle', array( 170, 120, 140 ) ),\\n\\tarray( 'Cedar & Amber Candle', '22.00', 'HL-CAN-CED', $c_candles, 'candle', array( 196, 170, 130 ) ),\\n\\tarray( 'Wooden Pull-Along Truck', '24.00', 'BW-TRUCK', $c_toys, 'toy', array( 220, 80, 60 ) ),\\n\\tarray( 'Little Yellow Bus', '19.50', 'BW-BUS', $c_toys, 'toy', array( 250, 190, 40 ) ),\\n\\tarray( 'Building Blocks (30 pcs)', '29.00', 'BW-BLOCKS', $c_toys, 'blocks', array( 0, 0, 0 ) ),\\n\\tarray( 'Merino Wool Scarf', '35.00', 'MT-SCARF', $c_textile, 'scarf', array( 120, 40, 50 ) ),\\n\\tarray( 'Linen Tea Towel', '9.00', '', $c_textile, '', array() ),\\n);\\nforeach ( $products as $p ) {\\n\\t$prod = new WC_Product_Simple();\\n\\t$prod->set_name( $p[0] ); $prod->set_regular_price( $p[1] ); $prod->set_status( 'publish' );\\n\\tif ( $p[2] ) { $prod->set_sku( $p[2] ); }\\n\\t$prod->set_category_ids( array( $p[3] ) );\\n\\t$prod->set_short_description( 'Demo product for Coder Vibe GPSR Product Safety.' );\\n\\tif ( $p[4] ) { $img = cvgpsr_demo_image( sanitize_title( $p[0] ), $p[4], $p[5] ); if ( $img ) { $prod->set_image_id( $img ); } }\\n\\t$prod->save();\\n}\\nif ( class_exists( 'WC_Admin_Notices' ) ) { WC_Admin_Notices::remove_all_notices(); }\\n\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.1","1.0.2"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3779526,"resolution":"1","location":"assets","locale":"","width":1280,"height":880},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3779526,"resolution":"2","location":"assets","locale":"","width":1280,"height":820},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3779526,"resolution":"3","location":"assets","locale":"","width":780,"height":351},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3779526,"resolution":"4","location":"assets","locale":"","width":1280,"height":1194}},"screenshots":{"1":"GPSR check with summary, quick fix and CSV import.","2":"Safety profile for a manufacturer.","3":"Product safety tab on the product edit screen.","4":"Product safety information on the product page."}},"plugin_section":[],"plugin_tags":[395,264911,62667,237366,286],"plugin_category":[45],"plugin_contributors":[289435],"plugin_business_model":[],"class_list":["post-396943","plugin","type-plugin","status-publish","hentry","plugin_tags-eu","plugin_tags-gpsr","plugin_tags-manufacturer","plugin_tags-product-safety","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_contributors-oozye2","plugin_committers-oozye2"],"banners":{"banner":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/banner-772x250.png?rev=3779507","banner_2x":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/banner-1544x500.png?rev=3779507","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/icon-128x128.png?rev=3779507","icon_2x":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/icon-256x256.png?rev=3779507","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/screenshot-1.png?rev=3779526","caption":"GPSR check with summary, quick fix and CSV import."},{"src":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/screenshot-2.png?rev=3779526","caption":"Safety profile for a manufacturer."},{"src":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/screenshot-3.png?rev=3779526","caption":"Product safety tab on the product edit screen."},{"src":"https:\/\/ps.w.org\/coder-vibe-gpsr-product-safety\/assets\/screenshot-4.png?rev=3779526","caption":"Product safety information on the product page."}],"raw_content":"<!--section=description-->\n<p>Since 13 December 2024 the EU General Product Safety Regulation (GPSR) asks every online listing of a consumer product sold into the EU to show:<\/p>\n\n<ul>\n<li>the manufacturer's name, postal address and electronic address (email or website)<\/li>\n<li>if the manufacturer is outside the EU, the same details for an EU-based responsible person<\/li>\n<li>information to identify the product, including a picture<\/li>\n<li>warnings and safety information, where the product needs them<\/li>\n<\/ul>\n\n<p>Typing the same manufacturer address into hundreds of products is slow and error-prone. This plugin lets you enter it once.<\/p>\n\n<h4>Profiles, not copy and paste<\/h4>\n\n<p>Add each manufacturer and EU responsible person you work with as a <strong>safety profile<\/strong>: legal name, trade name, postal address, country, email, website and phone. Then assign profiles where they apply:<\/p>\n\n<ul>\n<li><strong>Shop default<\/strong>: for shops that make everything themselves<\/li>\n<li><strong>Category<\/strong>: \"Toys\" uses one manufacturer, \"Candles\" another; sub-categories can override their parent<\/li>\n<li><strong>Brand<\/strong>: works with WooCommerce Brands<\/li>\n<li><strong>Product<\/strong>: for the exceptions<\/li>\n<\/ul>\n\n<p>Each product uses its own setting first, then its brand, then its categories, then the shop default. Change a manufacturer's address once and every product showing it updates.<\/p>\n\n<h4>Find what's missing<\/h4>\n\n<p><strong>Products \u2192 GPSR check<\/strong> scans every published product and lists exactly what is missing:<\/p>\n\n<ul>\n<li>no manufacturer, or an incomplete manufacturer profile<\/li>\n<li>a non-EU manufacturer without an EU responsible person<\/li>\n<li>a responsible person who isn't based in the EU<\/li>\n<li>no product image<\/li>\n<li>no SKU or GTIN, and no warnings (flagged for review, since not every product needs them)<\/li>\n<\/ul>\n\n<p><strong>Quick fix<\/strong> fills a chosen profile into every product missing one, in one click. A GPSR column on the products list shows the status of each product, and bulk edit can set or clear profiles on any selection.<\/p>\n\n<h4>CSV export and import<\/h4>\n\n<p>Export every product with its GPSR settings, fill the gaps in a spreadsheet and import it back. A test run shows what would change before anything is saved. Products are matched by ID or SKU, profiles by name.<\/p>\n\n<h4>On the product page<\/h4>\n\n<p>Shown in a \"Product safety\" tab, below the add to cart button, or wherever you place the <code>[cvgpsr_safety]<\/code> shortcode. The manufacturer is also added to the product's schema.org structured data, and all GPSR data is available read-only in the WooCommerce REST API (<code>gpsr<\/code> on <code>\/wp-json\/wc\/v3\/products<\/code>) for feed and marketplace tools.<\/p>\n\n<h4>Important<\/h4>\n\n<p>This plugin stores and displays the information you enter. It does not check that the information is correct, does not act as your EU responsible person, and is not legal advice. Whether a product falls under the GPSR, and what warnings it needs, depends on the product.<\/p>\n\n<h4>Need help setting it up?<\/h4>\n\n<p>If you have a large catalogue and would rather not fill it in yourself, Coder Vibe offers a paid setup service: creating your profiles and filling GPSR data across your products from your supplier information. Every feature of the plugin is free with or without it. See https:\/\/codervibe.co.uk.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate the plugin. WooCommerce must be active.<\/li>\n<li>Go to <strong>Products \u2192 Safety profiles<\/strong> and add your manufacturers and EU responsible persons.<\/li>\n<li>Set a shop-wide default in <strong>WooCommerce \u2192 Settings \u2192 Products \u2192 Product safety (GPSR)<\/strong>, and\/or set profiles on your categories and brands.<\/li>\n<li>Open <strong>Products \u2192 GPSR check<\/strong> and fix what's left, by hand, with Quick fix, or by CSV.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20an%20eu%20responsible%20person%3F\"><h3>Do I need an EU responsible person?<\/h3><\/dt>\n<dd><p>If the manufacturer of a product is established outside the EU, the GPSR requires an economic operator in the EU to be responsible for it, and their details must be on the listing. If you make your products yourself in the UK, that includes you. Specialist companies provide this service.<\/p><\/dd>\n<dt id=\"what%20language%20should%20the%20warnings%20be%20in%3F\"><h3>What language should the warnings be in?<\/h3><\/dt>\n<dd><p>The GPSR requires safety information in a language easily understood by consumers in the country where the product is sold. The plugin shows the text exactly as you write it.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20variable%20products%3F\"><h3>Does it work with variable products?<\/h3><\/dt>\n<dd><p>Yes. Variations use their parent product's GPSR information.<\/p><\/dd>\n<dt id=\"does%20it%20change%20anything%20at%20checkout%3F\"><h3>Does it change anything at checkout?<\/h3><\/dt>\n<dd><p>No. It only adds information to product pages, structured data and the REST API.<\/p><\/dd>\n<dt id=\"does%20it%20contact%20any%20external%20service%3F\"><h3>Does it contact any external service?<\/h3><\/dt>\n<dd><p>No.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>EU responsible person choices (product, category, brand, bulk edit, shop default and quick fix) now only list profiles based in the EU. A value saved earlier is still shown.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Developer hooks for add-ons: cvgpsr_issues, cvgpsr_issue_labels, cvgpsr_has_content, cvgpsr_render_extra, cvgpsr_product_panel, cvgpsr_save_product, cvgpsr_term_fields, cvgpsr_save_term, and Resolver::lookup().<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First release.<\/li>\n<\/ul>","raw_excerpt":"EU GPSR product safety information for WooCommerce shops with big catalogues: reusable profiles, inheritance, a missing-data report and CSV import.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/396943","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=396943"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/oozye2"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=396943"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=396943"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=396943"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=396943"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=396943"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=396943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}