Releases: johnjanney/block-converter-for-divi
Release list
v2.10.0 — two video defects, and a restore that checks before it replaces
Two video conversion defects, and a restore that checks the page before it replaces it.
Everything here comes from an external review of the code. Each finding, what
reproducing it showed, and what was done is recorded in the repository.
A video embed could be replaced with a different site's video
Both video paths decided which provider a URL belonged to by searching it for
youtube.com, youtu.be or vimeo.com as a substring. That is not a question
about the URL.
A page whose video came from a host such as notyoutube.com was converted into
a real YouTube embed of whatever identifier happened to sit in the address:
https://notyoutube.com/embed/WRONG -> https://www.youtube.com/watch?v=WRONG
https://notvimeo.com/video/123 -> https://vimeo.com/123
Somebody else's video, published on your page, with nothing in the conversion
report to say it had happened. The host is now parsed and matched against an
allowlist on a dot boundary — the exact host or a true subdomain — so
www.youtube-nocookie.com and player.vimeo.com still convert and the
lookalikes are left exactly as they were written.
Both paths now ask one shared function. Two copies of the same regex is why the
same defect existed twice.
An unusable video address survived inside the block
<video src="javascript:alert(1)"> was escaped out of the visible markup and
kept in the block's saved attributes — the half the editor rebuilds the markup
from, so the value that survived was the one that should not have. The address
is now cleaned before either use, and a source that does not survive that is not
a source: the tag is kept exactly as written in a Custom HTML block, and the
conversion says so.
Restore checks that the page is still the one you were shown
Restore replaces what is on the page. It used to do that unconditionally, on the
reasoning that you are explicitly discarding whatever is there — which is true
of the version you were looking at when you pressed the button, and only of that
version. A save that arrives after that was never on screen.
So restore now names the version it is replacing. If somebody saved the page
after your last scan, the restore is refused and nothing is written: look at
what the page holds now, then press Restore again if that is still what you
want.
This is why the release is 2.10.0 rather than 2.9.4. Nothing a user does
changes — the admin screen sends what it needs to — but d2g_restore_page is a
documented endpoint and its contract changed.
And an honest note about how far that check reaches
Conversion and restore both make their check at the last moment WordPress
allows, immediately before the write. That is microseconds wide where it used to
be however long a large page takes to convert, and it is not zero: a save that
lands inside that instant is still lost. The test suite now writes into that gap
through a second database connection and records what happens, so this is a
measured statement rather than a claim.
Run batch conversions while nobody is editing.
Nothing needs reconverting because of this release — but if a page you
already converted held a video, it is worth opening it and checking the video
is the one you meant.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Also in this release
- The internal encoding diagnostic — attached here, and not part of the
plugin — cleaned up after itself by deleting every post whose slug was
bcfd-import-probe-1or-2. On a site with a page of that name, it deleted
the page: permanently, with no trash and no revision. It now marks what it
creates and deletes only that, runs nothing until you press a button, and is
numbered 1.1.0. If you hold a copy of 1.0.0, delete it. - The development dependency
npm audithad been reporting as a High
vulnerability is gone, and the test environment now pins the WordPress version
it runs against so a gate cannot fail for reasons that are not this plugin. - Several project documents said things that had stopped being true.
Package: block-converter-for-divi-2.10.0.zip — 159,044 bytes
SHA-256: 22f5e433ac5fc003a69597f9cdf18631beeca76755632f5208a443fd7600fee5
Diagnostic: divi-quote-encoding-diagnostic.zip — 10,499 bytes, version 1.1.0
SHA-256: 4246a7af18c9c9eb82f4a7e0ba42434a74c864b9a5c848f124a8ee96f30e5ac2
v2.9.3 — a stale token is no longer a dead end in a batch
A page edited between being scanned and being converted no longer stops a batch.
What was wrong
Every conversion request names the version of the page it means to convert, and
the server refuses when that version is no longer there. That is right — it stops
a write landing on content nobody read.
But inside a batch of fifty, that row had nowhere to go. The only way forward was
to abandon the batch and scan again. On a real 247-page site it cost one page,
and the cause was a WordPress importer still rewriting image URLs — something
that settles within seconds.
What changed
The refusal stands, and still writes nothing. What is new is that it hands back
the page's current token, so the caller has somewhere to go.
The browser comes back exactly once with that token, and has to name the
version it gave up on. Once, because a page whose content keeps moving is
genuinely unstable, and retrying in a loop would hide that rather than report it.
When the version named is not the version converted, the conversion says so:
the page was edited between being scanned and being converted, what was converted
is not quite what the preview showed, compare it before relying on it, and the
backup holds the same version that was written.
Why not simply re-read on the server
That would have cleared the dead end just as well, and made the token
decorative. The server choosing which version to convert is the exact thing the
token exists to prevent.
It still means what it always meant: the caller states which version it is
converting, and the server never chooses for it. That is why the retry belongs
to the browser and why it has to declare what it supersedes.
Nothing needs reconverting because of this release.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.9.3.zip — 152,848 bytes
SHA-256: a3501e19138fd223cc0f33e08527130e25f324b536e1afc628523b45cd3ece93
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: 8,287 blocks valid against
core's own parser.
A test that had to change
a batch with one failure reports the failure separately failed on the first
run, correctly — it manufactured its failure using a stale token, and a stale
token is no longer a failure.
That test is the regression guard for the 2.0.0 defect where the batch runner
counted failed pages as successes, so weakening it to fit the new behaviour was
not available. It was given a failure that stays refused instead — a page that
already holds a previous conversion — and a second test now covers the
stale-token path end to end, including that the warning reaches the screen.
Verified
- Live suite: 50 checks, 0 failed (was 43)
- Browser suite: 10, 0 failed (was 9)
- Offline: 212 tests, 566 blocks through core's own validator
- WordPress 6.1 through 7.0.3 pass at 50/50; 6.0 refuses as declared
The browser suite carries more weight than usual here: half of this change lives
in admin.js, and the new test drives it against a real WordPress.
Also corrected
The plugin listing's answer to "Can I undo a conversion?" promised the backup
"is never replaced by a later conversion". That stopped being true in 2.9.2,
which refreshes a snapshot that has gone stale while its page is still Divi. It
now states the guarantee that does hold and always has: a conversion never
replaces the backup with converted content, so what comes back is the Divi
version of the page and never a copy of the blocks it became.
Note added 2026-08-12: the attached diagnostic was replaced
divi-quote-encoding-diagnostic.zip on this page is version 1.1.0, uploaded
after this release. It is not the file this release originally carried.
The 1.0.0 copy attached here cleaned up after its import probe by deleting every
post whose slug was bcfd-import-probe-1 or bcfd-import-probe-2, which is not
the same question as did this run create it. On a site with a page of that
name, it deleted the page — permanently, with no trash and no revision — while
its own header promised it touched no existing post. GitHub records no downloads
of that file.
1.1.0 marks what it creates and deletes only that, and runs nothing at all until
an administrator presses a button. If you hold a copy of 1.0.0, delete it.
The plugin archive on this page, block-converter-for-divi-2.9.3.zip, is
unchanged and is still the file whose digest is recorded above. The diagnostic
has never been part of the plugin.
Replaced rather than removed so that this page still hands you a working tool,
and noted here rather than swapped quietly, because a release that edits itself
without saying so is not a record.
v2.9.2 — a refused conversion no longer leaves a stale backup
A patch, about the backup a page keeps behind its Restore button.
What was wrong
Three things can refuse a conversion after the backup snapshot has already
been taken: a conversion that produces nothing, a KSES refusal on a site that
strips scripts, and the concurrency check that stops a write landing on content
it did not read.
A refusal left that snapshot behind, and the snapshot is write-once, so it
stayed. The next conversion of that page was then backed by a copy of content
that had moved on — worse than no backup, because it looks like one.
Not hypothetical: on a real 247-page site a page ended up with a rollback target
holding an image URL the WordPress importer had since renamed. Restoring it
would have put back an address that no longer resolved.
The fix, in two halves
A refusal puts back exactly what it found. write_backup() returns a record
of every key it touched, as it found it, and each of the three refusal paths
restores that record.
The first version of this deleted a snapshot it had created, and the live suite
rejected it — the refresh below can replace a snapshot that was already there,
and "delete what we added" cannot give that back. Restoring what was found is the
only form that holds in both cases; the two only look alike when there was
nothing there to begin with, which is not the case that bit.
A snapshot that has gone stale is refreshed. When a stored snapshot differs
from the page in front of it and that page is still Divi, the snapshot is
replaced. This repairs backups that are already stale, the next time such a
page is converted.
The write-once rule is untouched, and now checked rather than assumed. What it
existed to prevent was a second conversion replacing the original with
converted output, and that still cannot happen: the only content that may
replace a snapshot is Divi content, and a page holding a previous conversion
never overwrites its own. Both directions are asserted.
Nothing needs reconverting because of this release, and backups of pages
you have already converted are unaffected — those still hold the original Divi
content, which is what Restore is for.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.9.2.zip — 150,669 bytes
SHA-256: fa8e04a499bfe1a28f9f3432853a94a6544e49ed7b42975bdcafdc98891e1590
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: 8,287 blocks valid against
core's own parser.
Verified
- Live suite: 43 checks, 0 failed (was 36), 193/193 fixtures valid through a
real database, empty debug log - Offline: 212 tests, 566 blocks through core's own validator
- WordPress 6.1 through 7.0.3 all pass at 43/43; 6.0 refuses as declared
Seven new live assertions cover both directions, including "a refusal does not
delete a backup it did not create" and "a converted page cannot overwrite its
own snapshot".
All of it lives in the live suite because that is the only place it can be
tested — this is post-meta behaviour through the real endpoints, and nothing
offline reaches it.
Still open
A conversion refused inside a batch remains a dead end for that row: the message
says to scan again, and scanning again means abandoning the batch. Retry logic
does not belong in a data-safety fix without first settling how it avoids
papering over a genuine concurrent edit, which is the thing the guard is for.
v2.9.1 — the census miscounted an image used twice
A patch. The content census introduced in 2.9.0 reported images as missing that
were not.
What was wrong
It counted a page's images two different ways. Going in: a set of addresses, plus
the number of attachment IDs each gallery listed. Coming out: a single
deduplicated set of addresses.
So a photo used both on its own and inside a gallery on the same page counted
as two on the way in and one on the way out, and the page was told it had lost an
image. Seven pages on a real 247-page site, each reported as "1 of N missing",
every one of them wrong. Nothing was ever actually lost — the warning was, not
the conversion.
Both sides now count gallery contents the same way. Deduplicating one side and
counting the other is not a comparison, and that the two were written to
different rules is the real defect.
Why it shipped
The offline suite gives every attachment its own address, so the collision cannot
happen there. It needed a real media library — which is to say it needed 2.9.0 to
be run against one.
That is the second time in three releases that the thing standing between a
defect and the test suite was that fixtures are too tidy. The new fixture pins
the case to the attachment ID the suite actually resolves, so the collision is
real in the suite rather than described in a comment.
A census that cries wolf is worse than no census, because the whole value of it
is that a warning from it means something.
Nothing else changes, and pages converted with 2.9.0 do not need
reconverting. If you saw one of those "fewer images came out" warnings, your
page was fine.
Package: block-converter-for-divi-2.9.1.zip — 148,601 bytes
SHA-256: b0981031723f4e87d31842772426053dec9374431c091151970c99dbd752a0d4
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: 8,287 blocks valid against
core's own parser.
Verified
- 212 offline tests, 566 blocks through core's own validator
- 36 live tests against a real WordPress
- WordPress 6.1 through 7.0.3 pass; 6.0 refuses as declared
- No existing golden snapshot moved
- Replayed over all 247 real conversions from the run that found it: the
fixed census reports nothing, once the renderers' own acknowledged losses are
counted
A claim in this release was later disproved
This release originally reported that the run which found the miscount had also
settled where the " attribute encoding comes from, naming the WordPress
WXR importer. That was over-read from a before-and-after on a site with a large
plugin set active, and it is not correct.
Importing the same export into a clean WordPress with only this plugin and the
importer active produces no encoded delimiters at all, and wp_kses_post(),
the content_save_pre chain and a save without unfiltered_html all leave the
quotes alone. Something on that particular site does it — Divi is the obvious
suspect, being the plugin that owns et_pb_ content — but it is not established,
and "any Divi site moved by WXR import hits this" should not have been said.
What has not changed: the encoding is real, it did void 246 of 247 pages before
2.7.0, and the parser has tolerated it since — wherever it comes from.
Tracked as Q43 in the project's open questions; Q36 carries the correction.
v2.9.0 — the conversion counts itself
The last two releases were found by counting a real corpus by hand — 278
images that never arrived, 249 button links replaced by #, a press-coverage
section deleted along with its ten links. Every one of them was silent. This
release moves the counting into the plugin, so it happens on every conversion
and nobody has to think to look.
What it does
Every conversion is now counted in and counted out — words, links, images and
buttons — and any shortfall that the conversion did not already account for is
reported alongside the other warnings.
It shares no code with the parser, and that restriction is the whole value of
it rather than an implementation detail. A counter built on the parser would have
read [et_pb_button button_url="…"] as an attribute-less tag in exactly
the way the converter did before 2.7.0: zero links going in, zero coming out, and
a clean bill of health for a page that had lost every link on it. The duplicated,
naive regexes are deliberate — two implementations that fail the same way are one
implementation.
A renderer that drops something on purpose says so, and the census subtracts it:
the gallery images whose attachments are gone, a section's background image.
That subtraction is what makes the remainder worth reading. What is left is loss
that nothing in the converter knew about, which is precisely the class of
defect that produced 2.7.0 and 2.8.0.
It reports; it does not refuse. A count is evidence, not proof — a module that
legitimately becomes a placeholder loses words, and blocking a conversion over an
arithmetic difference would stop work you asked for.
It found something on its first run
Across 247 real pages it raised exactly one finding, and it was real rather
than noise:
[et_pb_video src="https://vimeo.com/591103025" src_webm="https://vimeo.com/500110446"]
The renderer read src_webm only as a fallback when src was empty, so the
second URL was dropped in silence. Usually that costs nothing, because Divi's two
sources are the same video in two encodings — but nothing obliges an author to
use them that way, and here they were two different films. core/video holds one
source, so this cannot be repaired; it is now reported so it can be rebuilt by
hand. After the fix the census reports nothing across all 247 pages.
It also surfaced two silent losses that had been in the fixture corpus for
releases, both now reported: modules nested inside a counter's label were reduced
to their words and the rest discarded (its existing warning is about the bar
animation, a different loss), and a social follow link naming no network was
skipped along with its address.
Nothing changes about pages you have already converted. What this adds is a
check that runs on every conversion from now on. If you see one of its
warnings, compare the page with the original before relying on it — and please
report it.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.9.0.zip — 147,685 bytes
SHA-256: 90571fc359541c7eac670956fac50eda346589a8fca534d44102628830915a19
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: identical output, 8,287
blocks valid against core's own parser.
Verified
- 211 offline tests (was 207), 561 blocks through core's own validator
- 36 live tests against a real WordPress, 192/192 fixtures valid after a
database round trip - Corpus: 247/247 valid, 8,287 blocks, zero census findings
- Block-library matrix: 192/192 valid on all nine releases, 6.1 → 7.0.2
- Version matrix: 6.1 through 7.0.3 pass, 6.0 refuses as declared
- Conversion costs about 13% more — 0.74 to 0.84 ms a page — which is what
reading every document twice buys
The suite gained a standing gate: no fixture may produce a census warning, so a
future change that silently drops content turns the run red on its own. It also
asserts that the census can still detect loss, because a counter that never fires
reads as proof.
What this does not do
It does not replace the corpus, the golden snapshots or the block validator. It
catches the one thing none of them can — loss the converter is blind to — and
nothing more.
The corpus remains real but narrow: 13 of the 58 supported modules. Tabs,
accordions, toggles, pricing tables, forms, blurbs, sliders, testimonials,
counters, maps, code and blog modules are still exercised only by synthetic
fixtures. Gallery images whose attachment IDs did not survive a move between
sites still cannot be recovered — Divi stores IDs and no URLs. And no converted
page has yet been compared visually against its Divi original.
v2.8.0 — the corpus, read a second time
The same 247-page corpus that produced 2.7.0, run a second time and read again.
It confirmed the previous release held — 285 of 285 converted pages valid
against core's own parser, 100% of content words preserved, 1,427 of
1,428 source URLs, no residual shortcodes. Then it said three more things.
Spacing now survives on the modules, not just the boxes around them
2.4.0 mapped custom_padding and custom_margin onto sections, rows and
columns and stopped at the containers. The corpus had 835 usable spacing
values sitting on the text, gallery, image, button, divider, video and audio
modules inside them, every one reported as lost. Core supports spacing on
every block those become; the only thing in the way was that nobody had
measured it.
| before | after | |
|---|---|---|
| Blocks carrying mapped design | 550 | 1,051 |
| Spacing reported lost | 718 | 0 |
| Warnings | 1,243 | 668 (5.0 → 2.7 a page) |
| Pages reporting any design loss | 247 | 182 |
What is left is hover styling (259) and positioning and transforms (235), which
core has no way to express, so they are reported and should be.
247 warnings that were wrong
Two causes, both there since 2.4.0. custom_padding__hover matched the spacing
pattern before the hover pattern did, so a column whose padding had been
carried still announced it was lost. And Divi writes custom_padding="|||" for
padding that was never set — a quarter of every spacing attribute in the corpus
— which sent people off to rebuild padding that had never existed.
Converting a conversion is refused
One page in the 2.7.0 run had been converted from 2.6.0's output rather than
from its restored Divi. It was reproduced byte for byte, and it had lost its
donation button and an image and gained the words wp:paragraph printed on the
page. It was reachable because a conversion that leaves a shortcode behind
leaves the row looking convertible.
The obvious guard is wrong twice, and the corpus caught both: testing for
<!-- wp: would refuse the 142 pages carrying Divi 5's placeholder comment,
which convert perfectly, and would refuse a page with a block comment inside a
module, which the converter has stripped and reported since 2.2.0. So the
question asked is structural — a cheap pattern decides whether it is worth
asking, then the parser decides whether the delimiter is the document's own
structure or a module's content. None of the 247 restored Divi pages are
refused; all 247 previous outputs are.
Pages you already converted do not gain module spacing by upgrading.
Restore one from its backup and convert it again to pick it up — that needs a
backup to exist and it replaces any block editor work done since. Convert
one page and compare it with the original before deciding for the rest.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.8.0.zip — 139,529 bytes
SHA-256: ebd31a36740184a0735074b2dec4622f5a9d5bd182f5100a39327831c5a4f41c
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: identical output, 8,287
blocks valid against core's own parser.
What is still reported rather than carried
- Gallery images no longer in the media library. A Divi gallery stores
attachment IDs and no URLs, so there is nothing to fall back on. On the corpus
207 of 250 galleries came out empty because only 11 of 2,699 IDs survived a
move between sites. Reported loudly; not repairable. Convert before moving a
site between installs, not after. - Hover states, positioning and transforms, per-device overrides, animations and
module custom CSS. - A corner radius that differs per corner, border widths per side, font family
and weight.
What this release does and does not prove
Three traps were found by measuring each block with canonical.mjs instead of
assuming: core/video and core/audio write the style attribute before the
class where image, gallery and separator write it after; core/separator emits
spacing before its colour declarations, the reverse of a group; and a Text
module becomes several sibling blocks, so it has nothing of its own to hold
padding and gains a core/group — only where there is spacing to carry.
Verified: 207 offline tests and 36 live, the corpus at 247/247 valid, 8,287
blocks, every mapped block library from 6.1 to 7.0.2 accepting the markup, and
WordPress 6.1 through 7.0.3 passing while 6.0 refuses as declared. No existing
golden snapshot moved — every diff is an addition, which is what says the
spacing work only touches content that carries spacing.
The corpus is real but narrow: 13 of the 58 supported modules. Tabs,
accordions, toggles, pricing tables, forms, blurbs, sliders, testimonials,
counters, maps, code and blog modules remain unexercised on real content.
And the comparisons that found all of this are still run by hand — the automated
in-versus-out counter that would catch this class of defect on the first page
converted does not exist yet.
v2.7.0 — what a real corpus found, and the five losses it fixed
The first release measured against real content at scale — 247 Divi pages from a
live site, converted and then read back out of the database. It found that the
converter had been discarding almost everything it was meant to carry.
Divi content reaches this plugin having been through storage paths that write a
shortcode attribute's delimiting quotes as ". Against that input the
attribute parser matched nothing at all, so every module rendered from an
empty attribute array — and the loss reporter, which works by inspecting
parsed attributes, had nothing to report. Measured on the corpus, where 246 of
247 pages were affected:
| before | after | |
|---|---|---|
et_pb_image → image blocks |
0 of 278 | 278 |
| Buttons keeping their link and label | 3 of 252 | 252 |
| Column widths carried | 24 | 533 |
| Blocks carrying mapped design | 17 | 550 |
| Warnings raised (of 1,243 due) | 783 | 1,243 |
| Word tokens lost | 444 | 0 |
249 buttons had become <a href="#">Click Here</a>; 207 of them had pointed at
a donation page.
If your pages converted correctly before, this release changes nothing about
them. If they did not, upgrading does not repair them — the damage is in
the stored content. Restore an affected page from its backup and convert it
again, which needs a backup to exist and replaces any block editor work done
since. Convert one page and compare it with the original before deciding for
the rest.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.7.0.zip — 133,440 bytes
SHA-256: f44f44bd56363f7aabd5a3e56358a5cf2b56b59267d4142c1b91d104247add73
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive over all 247 corpus pages: identical output, 8,020
blocks valid against core's own parser.
Also fixed, all of them silent
- A tag mixing both quote forms ran off the end of the document — it
swallowed the closing]and published the rest of the page as raw shortcode
text. Two pages did this, one printing a Mailchimp list ID onto the page. - Gallery images whose attachment ID no longer resolves were dropped without
a word; 202 of 243 galleries emptied that way. Now reported, with a distinct
message when nothing is left. - A sub-list written beside its
<li>rather than inside it was dropped
whole — one page lost 173 words and 10 press-coverage links.
Added
- Pages built with Divi 5 are counted and reported. Divi 5 stores pages as
wp:divi/*blocks, not shortcodes, so a scan looking for[et_pb_could not
see them: 19 pages were listed nowhere and stayed Divi-dependent while the
screen implied the site was done. They are counted, not converted — that
is a separate feature.
What is still reported rather than carried
- Gallery images that are no longer in your media library cannot be
recovered. A Divi gallery stores attachment IDs and no URLs, so there is no
address to fall back on. Convert before moving a site between installs, not
after — an import renumbers attachments. - A border radius that differs per corner, and border widths set per
side. - Font family and weight — Divi packs them into one undocumented value.
- Headings outside the Text module, hover states, transforms, parallax,
animations, per-device overrides, and module custom CSS.
What this release does and does not prove
189 passing tests, 58/58 module coverage and byte-exact golden snapshots did not
catch the defect above, because every fixture had been written with canonical
" delimiters — which is what someone writing a fixture types. The corpus was
not merely incomplete; it was biased toward the one input shape that already
worked. That is the real lesson here, and it is why this release exists.
The corpus is real but narrow: 13 of the 58 supported modules. Tabs,
accordions, toggles, pricing tables, forms, blurbs, sliders, testimonials,
counters, maps, code and blog modules remain unexercised on real content,
and those are where conversion has the least to work with.
Verified this release: 197 offline tests (was 189) and 30 live (was 23); the
whole 247-page corpus valid against core's own parser; every mapped block
library from 9.0.8 to 9.40.2 accepting the markup; WordPress 6.1 through 7.0.3
passing while 6.0 still refuses as declared. The live suite now covers the scan
endpoint, which had no coverage of any kind before.
That is thorough evidence for block validity — that the editor opens these
pages without complaint — and, for the first time, real evidence about content
survival: zero word loss and all 1,189 source URLs preserved across the
corpus. It is still not a pixel comparison of a converted page against the
Divi original, which remains the largest open gap.
v2.6.0 — borders, completing the block-supports phases
Borders now survive conversion — width, colour, style, and corner radius where
all four corners match. This completes the block-supports work: spacing
(2.4.0), typography (2.5.0) and now borders are carried onto core's own
block settings rather than reported as lost, so they appear in the editor
sidebar and stay editable.
Converted pages from before 2.4.0 kept none of the three. Restoring such a
page from its backup and converting it again picks all of them up at once —
which needs a backup to exist, and replaces any block editor work done since.
Convert one page and compare it with the original before deciding for the
rest.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.6.0.zip — 127,934 bytes
SHA-256: 3a293763d8921b550a0609890dc3bb9af59f99fe333436212b60ebb4462d00ba
Every file in the archive is byte-identical to this tag, and the converter was
run out of the archive to confirm all three phases work in what actually
ships.
What is still reported rather than carried
- A border radius that differs per corner. Divi does not document which
corner it lists first, and CSS shorthand order is not a safe assumption.
Rounding the wrong corner of your box would be worse than saying so. - Border widths set per side.
- Font family and weight — Divi packs them into one undocumented value.
- Headings outside the Text module, hover states, transforms, parallax,
animations, per-device overrides, and module custom CSS.
What "verified" means here, and what it does not
Every phase was measured against core's own serializer and validated on the
block library of all nine supported WordPress releases, plus a browser run and a
live WordPress. That is thorough evidence for block validity — that the
editor will open these pages without complaint.
It is not evidence of visual fidelity. Nothing in this project has yet
compared a converted page with the Divi original. That remains the largest open
gap, and it is why the upgrade notice says to convert one page and look.
The third and last of the block-supports phases. Spacing (2.4.0), typography
(2.5.0) and now borders are carried onto core's own block supports rather than
reported as lost.
Added
-
Divi borders on sections, rows and columns are carried over.
border_width_all,border_color_allandborder_style_allbecome core's
style.border.*, andborder_radiibecomesstyle.border.radiuswhen all
four corners carry the same value.A radius that differs per corner is not guessed at. Core keys its corners
topLeft/topRight/bottomLeft/bottomRight, CSS shorthand runs
top-left/top-right/bottom-right/bottom-left, and Divi's order is documented
nowhere. Rounding the wrong corner of somebody's box on the strength of a
guess is worse than saying it was not carried over, so the renderer raises a
warning naming that specific case — more use than the generic "borders" line
it replaces. Three corners set and one empty is treated the same way.Per-side border widths are still reported as lost.
Fixed
- The declaration order comment on
wrapper_styles()said the order was
alphabetical. It is not: measured with border, background and spacing
together, core emits border before background, which alphabetically follows
it. The earlier phases got away with the wrong explanation because nothing
they emitted could tell the difference.
v2.5.0 — typography and text colour survive conversion
Typography and text colour now survive conversion. Divi's body text colour, font
size, line height and letter spacing become the block editor's own typography
settings — so they appear in the sidebar and stay editable, rather than being
frozen into markup.
Pages converted before 2.4.0 kept neither spacing nor typography, and are not
retro-fixed. Getting either means restoring that page from its backup and
converting it again — which needs a backup to exist, and replaces any block
editor work done since. With two feature releases behind it, that is worth
deciding deliberately rather than page by page.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.5.0.zip — 125,868 bytes
SHA-256: b3f3d98e10f2b6aafd4a798c06a97006b7de57af4fe3734a2537699b05e77190
Every file in the archive is byte-identical to this tag.
Two limits, stated rather than buried
Headings outside the Text module still report their typography as lost. Only
the Text module builds its headings through the HTML engine; every other module
builds them from an attribute and never receives header_font_size at all.
Determined by running eight modules through the converter and looking, not by
assuming a family behaves alike.
Font family and weight are not carried over. Divi packs family, weight, style
and transform into one pipe-delimited value whose format is undocumented. Getting
it wrong puts the wrong font on your page, so it is reported instead of guessed.
Nothing here is guessed
Core serializes these declarations in a specific order, and a block that lists
them differently is reported as containing unexpected content. The order was
measured by asking core what it would have saved, then validated against the
block library of all nine supported releases, 6.1 through 7.0.2.
The second release that repairs a design setting rather than reporting it, and
the one that makes converted body text look like the original.
Added
-
Divi typography and text colour are carried over where a module renders its
body through the HTML engine.body_text_color,body_font_size,
body_line_heightandbody_letter_spacingbecome core'sstyle.color.text
andstyle.typography.*on the paragraphs that body produces; the Text
module'sheader_*equivalents do the same for its headings.Core serializes these alphabetically by CSS property — colour, font size,
letter spacing, line height — which was measured withtests/js/canonical.mjs
rather than assumed, and the result is validated on all nine supported
WordPress releases.line-heightgets its own grammar, because it is the one typographic value
CSS defines as valid without a unit and Divi writes it both ways. Everything
else goes through the same length grammar as spacing, so
body_font_size="18px;position:fixed"contributes nothing. -
The loss reporter narrows per module rather than globally.
header_*
typography reaches only the Text module — every other module builds its
headings in its own renderer from an attribute and never sees the setting — so
a CTA now carries its body typography and still reports the heading typography
it loses. Which modules those are was determined by measurement, not by
assuming a whole renderer family behaves alike.
Known
header_fontandbody_fontare still reported as lost. Divi packs family,
weight, style and transform into one pipe-delimited value whose grammar is not
documented, and this project has not seen enough real examples to encode it.
Getting it wrong would put the wrong font weight on somebody's page, which is
worse than saying it was not carried over.
v2.4.0 — spacing and background colour now survive conversion
The first release that repairs a design setting rather than reporting it.
Section, row and column spacing and background colour are now carried over.
Divi's custom_padding and custom_margin become the block editor's own
spacing settings, so they appear in the sidebar and stay editable rather than
being frozen into markup.
Already converted pages are not retro-fixed. Pages converted with an
earlier version kept none of this spacing. To get it, restore such a page from
its backup and convert it again — but only if it has a backup, and only if you
are willing to lose block editor work done since, because restoring replaces
the current content.
Upgrading from "Divi to Gutenberg Converter" (1.x)? Deactivate and delete
the old plugin first. The two cannot run together. Your backups live on your
posts, not in the plugin, so they survive.
Package: block-converter-for-divi-2.4.0.zip — 123,033 bytes
SHA-256: 820aa7abb2d2523101b2193206c1564bc4105f71faf1672c0eacacb82e8559f1
Every file in the archive is byte-identical to this tag.
Nothing here is guessed
Emitting an inline style a block's own save() would not have produced is
exactly what makes WordPress report "unexpected or invalid content". So the
markup, and the order of its declarations, were measured by asking core what it
would have saved — then validated against the block library of all nine
supported WordPress releases, 6.1 through 7.0.2.
Divi values go through a length grammar rather than an escape:
custom_padding="20px;position:fixed|||" contributes nothing. auto is dropped
too — legal CSS, but not a length core's spacing support can regenerate. A
background colour switched off in Divi is not painted.
Still report-only
Hover states, transforms, parallax, animations, per-device _tablet/_phone
overrides, and module custom CSS. WordPress has no block equivalent for any of
them, so they are named in the preview rather than half-converted.
The first release that repairs a design setting rather than reporting it. Every
converted page used to say "Design settings were not carried over: spacing
(padding or margin)" and leave the user to rebuild by hand what the Divi source
had stated precisely.
Added
-
Divi spacing and background colour on sections, rows and columns are now
carried over instead of reported as lost.custom_paddingandcustom_margin
become core'sstyle.spacingsupport;background_colorbecomes
style.color.background.Naively emitting an inline
styleis what makes a static block report
"unexpected or invalid content", so nothing here is invented. The declaration
order — background, then margin, then padding, each top/right/bottom/left,
withflex-basislast on a column — was measured withtests/js/canonical.mjs
by asking core what it would have saved, and the output is validated against
the block library of all nine supported WordPress releases.Divi values are validated rather than escaped.
css_length()takes a number
and a known unit;custom_padding="20px;position:fixed|||"contributes
nothing.autois dropped as well — legal CSS, but not a length core's
spacing support can regenerate. Empty components stay empty rather than
becoming0px, because writing zero where Divi wrote nothing flattens
spacing the theme was supplying.A background colour with
background_enable_color="off"is not painted.
Changed
- The loss reporter no longer names spacing on the modules that now keep it.
Renderers declare what they map viamapped_style_attrs()and the converter
reads that, so the report and the mapping cannot drift. Matching is by exact
attribute name:custom_paddingwent quiet,custom_padding_tabletdid not,
because core block supports have no responsive dimension. A report that fires
for settings the converter did carry over is how users learn to ignore it.