diff --git a/.plugin-data b/.plugin-data index cbe56a6..5fc57fc 100644 --- a/.plugin-data +++ b/.plugin-data @@ -1,4 +1,4 @@ { - "version": "1.0.2", + "version": "1.1.0", "slug": "blockparty-iframe" } diff --git a/.wp-env.json b/.wp-env.json index 0967ef4..dafab87 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1 +1,5 @@ -{} +{ + "plugins": [ + "." + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index c5757c6..3397328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,73 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/). -## [1.0.0] - 2025-01-09 +## [1.1.0] - 2026-01-12 + +### 🚀 Added + +#### Iframe Code Parsing + +- **HTML iframe code support**: You can now paste complete iframe HTML code instead of just URLs +- **Automatic URL extraction**: The `src` attribute is automatically extracted from iframe code +- **Auto-fill title**: The `title` attribute is automatically extracted and fills the title field +- **Custom attributes preservation**: All custom iframe attributes are extracted and preserved in the final output +- **Smart content filtering**: HTML content inside iframe tags is automatically removed during parsing + +#### Attribute Management + +- **Custom attributes storage**: New `iframeAttributes` block attribute (array of key-value objects) +- **Boolean attributes support**: Proper handling of HTML boolean attributes like `allowfullscreen` +- **React prop mapping**: Automatic conversion of HTML attributes to React prop names + - `allowfullscreen` → `allowFullScreen` + - `allowpaymentrequest` → `allowPaymentRequest` + - `referrerpolicy` → `referrerPolicy` +- **Deprecated attributes filtering**: Automatic exclusion of deprecated HTML attributes + - `frameborder`, `marginwidth`, `marginheight`, `scrolling`, `align`, `longdesc` +- **Problematic attributes filtering**: Exclusion of attributes that cause issues in React + - `style` (requires object format in React) + - `name` (can cause conflicts) + - `width`, `height` (managed by block dimension supports) + +#### Code Quality + +- **Utility functions**: New `utils.js` module for shared functionality +- **Code refactoring**: Eliminated duplication between `edit.js` and `save.js` +- **Better error handling**: Improved parsing with `insertAdjacentHTML` for safer DOM manipulation + +### 🔧 Changed + +- **Source field enhancement**: The URL field now accepts both URLs and iframe HTML code +- **Placeholder text**: Updated to reflect dual input capability +- **Help text**: Added contextual help explaining the new paste functionality + +### 📚 Technical Details + +- **New file**: `src/blockparty-iframe/utils.js` with exported utility functions: + - `parseIframeCode()`: Parse iframe HTML and extract attributes + - `convertAttributesToProps()`: Convert attributes array to React props + - `mapHtmlAttributeToReact()`: Map HTML attribute names to React prop names + - `isBooleanAttribute()`: Identify boolean HTML attributes + - `isExcludedIframeAttribute()`: Filter out problematic attributes + +--- + +## [1.0.2] - 2026-01-10 + +### 🔧 Changed + +- **Release workflow**: Updated GitHub Actions release workflow configuration + +--- + +## [1.0.1] - 2026-01-09 + +### 🐛 Fixed + +- **Build artifacts**: Updated `.distignore` and `.gitattributes` for proper release packaging + +--- + +## [1.0.0] - 2026-01-09 ### 🎉 Initial Release @@ -59,4 +125,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). --- +[1.1.0]: https://github.com/BeAPI/blockparty-iframe/releases/tag/1.1.0 +[1.0.2]: https://github.com/BeAPI/blockparty-iframe/releases/tag/1.0.2 +[1.0.1]: https://github.com/BeAPI/blockparty-iframe/releases/tag/1.0.1 [1.0.0]: https://github.com/BeAPI/blockparty-iframe/releases/tag/1.0.0 diff --git a/blockparty-iframe.php b/blockparty-iframe.php index 507088b..8dddc13 100644 --- a/blockparty-iframe.php +++ b/blockparty-iframe.php @@ -2,7 +2,7 @@ /** * Plugin Name: Blockparty Iframe * Description: Add a block to display an embedded frame in the WordPress editor. - * Version: 1.0.2 + * Version: 1.1.0 * Requires at least: 6.7 * Requires PHP: 8.1 * Author: Be API Technical team @@ -19,7 +19,7 @@ exit; // Exit if accessed directly. } -define( 'BLOCKPARTY_IFRAME_VERSION', '1.0.2' ); +define( 'BLOCKPARTY_IFRAME_VERSION', '1.1.0' ); define( 'BLOCKPARTY_IFRAME_URL', plugin_dir_url( __FILE__ ) ); define( 'BLOCKPARTY_IFRAME_DIR', plugin_dir_path( __FILE__ ) ); define( 'BLOCKPARTY_IFRAME_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); diff --git a/composer.lock b/composer.lock index 8982730..4ab0fa6 100644 --- a/composer.lock +++ b/composer.lock @@ -1901,12 +1901,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "842120a67d0a2cc4f1b70f67683f4f541fec9711" + "reference": "ccfd723dc03e9864008d011603c412910180d7a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/842120a67d0a2cc4f1b70f67683f4f541fec9711", - "reference": "842120a67d0a2cc4f1b70f67683f4f541fec9711", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/ccfd723dc03e9864008d011603c412910180d7a6", + "reference": "ccfd723dc03e9864008d011603c412910180d7a6", "shasum": "" }, "conflict": { @@ -2435,7 +2435,7 @@ "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", "october/october": "<3.7.5", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<3.7.5", + "october/system": "<=3.7.12|>=4,<=4.0.11", "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", @@ -2901,7 +2901,7 @@ "type": "tidelift" } ], - "time": "2026-01-08T22:06:12+00:00" + "time": "2026-01-09T19:06:26+00:00" }, { "name": "roots/wordpress-no-content", diff --git a/languages/blockparty-iframe-fr_FR-0e3cdd73079dd5ef56b82e4a974c848c.json b/languages/blockparty-iframe-fr_FR-0e3cdd73079dd5ef56b82e4a974c848c.json index 99a577f..eb610b0 100644 --- a/languages/blockparty-iframe-fr_FR-0e3cdd73079dd5ef56b82e4a974c848c.json +++ b/languages/blockparty-iframe-fr_FR-0e3cdd73079dd5ef56b82e4a974c848c.json @@ -1 +1 @@ -{"translation-revision-date":"2025-12-24 15:10+0100","generator":"WP-CLI\/2.11.0","source":"build\/blockparty-iframe\/index.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"fr_FR","plural-forms":"nplurals=2; plural=(n != 1);"},"Replace":["Remplacer"],"View":["Voir"],"Settings":["R\u00e9glages"],"Enable lazyloading":["Activer le chargement diff\u00e9r\u00e9"],"Iframe":["Cadre"],"Fill the URL and the title of the iframe.":["Veuillez saisir une URL et un titre pour le cadre."],"URL":["URL"],"The URL is invalid.":["L\u2019URL n\u2019est pas valide."],"Title":["Titre"],"The title of the iframe is used for accessibility purposes and it will only be visible to screen readers.":["Le titre du cadre est utilis\u00e9 \u00e0 des fins d\u2019accessibilit\u00e9 et sera uniquement visible pour les technologies d\u2019assistance."],"Add iframe":["Ajouter le cadre"]}}} +{"translation-revision-date":"2026-01-12 11:32+0100","generator":"WP-CLI\/2.11.0","source":"build\/blockparty-iframe\/index.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"fr_FR","plural-forms":"nplurals=2; plural=(n != 1);"},"Replace":["Remplacer"],"View":["Voir"],"Settings":["R\u00e9glages"],"Enable lazyloading":["Activer le chargement diff\u00e9r\u00e9"],"Iframe":["Cadre"],"Fill the iframe source and the title of the iframe.":["Veuillez remplir la source du cadre ainsi que son titre."],"Source":["Source"],"You can either paste a URL or the iframe code.":["Vous pouvez soit renseigner une URL soit le code HTML du cadre."],"Title":["Titre"],"The title of the iframe is used for accessibility purposes and it will only be visible to screen readers.":["Le titre du cadre est utilis\u00e9 \u00e0 des fins d\u2019accessibilit\u00e9 et ne sera visible que par les technologies d\u2019assistance."],"Add iframe":["Ajouter un cadre"]}}} \ No newline at end of file diff --git a/languages/blockparty-iframe-fr_FR.mo b/languages/blockparty-iframe-fr_FR.mo index 7f2eb30..0e51288 100644 Binary files a/languages/blockparty-iframe-fr_FR.mo and b/languages/blockparty-iframe-fr_FR.mo differ diff --git a/languages/blockparty-iframe-fr_FR.po b/languages/blockparty-iframe-fr_FR.po index 68bcc71..a0a5781 100644 --- a/languages/blockparty-iframe-fr_FR.po +++ b/languages/blockparty-iframe-fr_FR.po @@ -1,15 +1,16 @@ msgid "" msgstr "" "Project-Id-Version: Blockparty Iframe 1.0.0\n" -"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/blockparty-iframe\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/blockparty-" +"iframe\n" +"POT-Creation-Date: 2026-01-12T10:29:55+00:00\n" +"PO-Revision-Date: 2026-01-12 11:32+0100\n" "Last-Translator: \n" "Language-Team: \n" +"Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2025-12-24T14:09:32+00:00\n" -"PO-Revision-Date: 2025-12-24 15:10+0100\n" -"Language: fr_FR\n" "X-Generator: Poedit 3.8\n" "X-Domain: blockparty-iframe\n" @@ -21,13 +22,62 @@ msgstr "Blockparty Cadre" #. Description of the plugin #: blockparty-iframe.php msgid "Add a block to display an embedded frame in the WordPress editor." -msgstr "Ajoute un bloc pour afficher un cadre intĂ©grĂ© dans l’éditeur WordPress." +msgstr "" +"Ajoute un bloc pour afficher un cadre intĂ©grĂ© dans l’éditeur WordPress." #. Author of the plugin #: blockparty-iframe.php msgid "Be API Technical team" msgstr "L’ équipe technique de Be API" +#: build/blockparty-iframe/index.js:1 +msgid "Replace" +msgstr "Remplacer" + +#: build/blockparty-iframe/index.js:1 +msgid "View" +msgstr "Voir" + +#: build/blockparty-iframe/index.js:1 +msgid "Settings" +msgstr "RĂ©glages" + +#: build/blockparty-iframe/index.js:1 +msgid "Enable lazyloading" +msgstr "Activer le chargement diffĂ©rĂ©" + +#: build/blockparty-iframe/index.js:1 +msgid "Iframe" +msgstr "Cadre" + +#: build/blockparty-iframe/index.js:1 +msgid "Fill the iframe source and the title of the iframe." +msgstr "Veuillez remplir la source du cadre ainsi que son titre." + +#: build/blockparty-iframe/index.js:1 +msgid "Source" +msgstr "Source" + +#: build/blockparty-iframe/index.js:1 +msgid "You can either paste a URL or the iframe code." +msgstr "Vous pouvez soit renseigner une URL soit le code HTML du cadre." + +#: build/blockparty-iframe/index.js:1 +msgid "Title" +msgstr "Titre" + +#: build/blockparty-iframe/index.js:1 +msgid "" +"The title of the iframe is used for accessibility purposes and it will only " +"be visible to screen readers." +msgstr "" +"Le titre du cadre est utilisĂ© Ă  des fins d’accessibilitĂ© et ne sera visible " +"que par les technologies d’assistance." + +#: build/blockparty-iframe/index.js:1 +msgid "Add iframe" +msgstr "Ajouter un cadre" + #: build/blockparty-iframe/block.json msgctxt "block title" msgid "Iframe" diff --git a/languages/blockparty-iframe.pot b/languages/blockparty-iframe.pot index e9719e9..2a65649 100644 --- a/languages/blockparty-iframe.pot +++ b/languages/blockparty-iframe.pot @@ -1,15 +1,15 @@ -# Copyright (C) 2025 Be API Technical team +# Copyright (C) 2026 Be API Technical team # This file is distributed under the GPL-2.0-or-later. msgid "" msgstr "" -"Project-Id-Version: Blockparty Iframe 1.0.0\n" +"Project-Id-Version: Blockparty Iframe 1.1.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/blockparty-iframe\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2025-12-24T14:09:32+00:00\n" +"POT-Creation-Date: 2026-01-12T10:29:55+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.11.0\n" "X-Domain: blockparty-iframe\n" @@ -50,15 +50,15 @@ msgid "Iframe" msgstr "" #: build/blockparty-iframe/index.js:1 -msgid "Fill the URL and the title of the iframe." +msgid "Fill the iframe source and the title of the iframe." msgstr "" #: build/blockparty-iframe/index.js:1 -msgid "URL" +msgid "Source" msgstr "" #: build/blockparty-iframe/index.js:1 -msgid "The URL is invalid." +msgid "You can either paste a URL or the iframe code." msgstr "" #: build/blockparty-iframe/index.js:1 diff --git a/package-lock.json b/package-lock.json index 8b3e35a..8303e38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "blockparty-iframe", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "blockparty-iframe", - "version": "1.0.0", + "version": "1.1.0", "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/block-editor": "latest", diff --git a/package.json b/package.json index b76d1fc..7d94b4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blockparty-iframe", - "version": "1.0.2", + "version": "1.1.0", "description": "Add a block to display an embedded frame in the WordPress editor.", "author": "Be API Technical team", "license": "GPL-2.0-or-later", @@ -12,6 +12,8 @@ "lint:js": "wp-scripts lint-js", "packages-update": "wp-scripts packages-update", "plugin-zip": "wp-scripts plugin-zip", + "make-pot": "wp i18n make-pot . languages/blockparty-iframe.pot --exclude=\"src\" --domain=blockparty-iframe", + "make-json": "wp i18n make-json languages/blockparty-iframe-fr_FR.po languages/ --no-purge", "start": "wp-scripts start --blocks-manifest", "start:env": "wp-env start --config=./wp-env.json", "stop:env": "wp-env stop --config=./wp-env.json" diff --git a/src/blockparty-iframe/block.json b/src/blockparty-iframe/block.json index b70d68a..b18ae93 100644 --- a/src/blockparty-iframe/block.json +++ b/src/blockparty-iframe/block.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "blockparty/iframe", - "version": "1.0.2", + "version": "1.1.0", "title": "Iframe", "category": "widgets", "description": "Display an embedded frame.", @@ -28,6 +28,21 @@ "url": { "type": "string", "default": "" + }, + "iframeAttributes": { + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } } }, "textdomain": "blockparty-iframe", diff --git a/src/blockparty-iframe/edit.js b/src/blockparty-iframe/edit.js index 102c424..c66694e 100644 --- a/src/blockparty-iframe/edit.js +++ b/src/blockparty-iframe/edit.js @@ -39,6 +39,7 @@ import { useState } from '@wordpress/element'; import './editor.scss'; import { aspectRatio } from '@wordpress/icons'; +import { convertAttributesToProps, parseIframeCode } from './utils'; /** * The edit function describes the structure of your block in the context of the @@ -50,12 +51,18 @@ import { aspectRatio } from '@wordpress/icons'; */ export default function Edit( { attributes, setAttributes } ) { const blockProps = useBlockProps(); - const { lazyload, title: initialTitle, url: initialUrl } = attributes; + const { + lazyload, + title: initialTitle, + url: initialUrl, + iframeAttributes: initialAttributes, + } = attributes; // State local pour les champs TextControl const [ iframeData, setIframeData ] = useState( { url: initialUrl || '', title: initialTitle || '', + iframeAttributes: initialAttributes || [], } ); // hasConfirmed = l’utilisateur a validĂ© l’ajout de l’iframe @@ -76,6 +83,29 @@ export default function Edit( { attributes, setAttributes } ) { const showPlaceholder = ! hasConfirmed; const showIframe = hasConfirmed && isIframeElligible; + // Handle URL/iframe code change + function handleUrlChange( value ) { + // Try to parse as iframe code + const parsed = parseIframeCode( value ); + + if ( parsed ) { + // It's an iframe code, extract URL, title, and attributes + setIframeData( { + ...iframeData, + url: parsed.url, + title: parsed.title || iframeData.title, // Use extracted title if available, otherwise keep current + iframeAttributes: parsed.attributes, + } ); + } else { + // It's a regular URL + setIframeData( { + ...iframeData, + url: value, + iframeAttributes: [], + } ); + } + } + // Handle clic Add iframe function handleAddIframeButtonClick() { setAttributes( { ...attributes, ...iframeData } ); @@ -118,28 +148,20 @@ export default function Edit( { attributes, setAttributes } ) { icon={ aspectRatio } label={ __( 'Iframe', 'blockparty-iframe' ) } instructions={ __( - 'Fill the URL and the title of the iframe.', + 'Fill the iframe source and the title of the iframe.', 'blockparty-iframe' ) } >
- setIframeData( { ...iframeData, url: value } ) - } - placeholder="https://..." - type="url" - help={ - iframeData.url.length && - ! isURL( iframeData.url ) - ? __( - 'The URL is invalid.', - 'blockparty-iframe' - ) - : '' - } + onChange={ handleUrlChange } + placeholder={ `https://... or ' ); + const iframeElement = tempDiv.querySelector( 'iframe' ); + + if ( ! iframeElement ) { + return null; + } + + // Extract src attribute + const src = iframeElement.getAttribute( 'src' ) || ''; + + // Extract title attribute + const title = iframeElement.getAttribute( 'title' ) || ''; + + // Extract all other attributes (excluding managed and deprecated ones) + const attributes = []; + + for ( const attr of iframeElement.attributes ) { + if ( ! isExcludedIframeAttribute( attr.name ) ) { + // For boolean attributes, store 'true' as value if present + const value = isBooleanAttribute( attr.name ) ? 'true' : attr.value; + + attributes.push( { + key: attr.name, + value, + } ); + } + } + + return { + url: src, + title, + attributes, + }; +}