Files
intotheeast-com-content/plugins/migrate-grav/CHANGELOG.md
T

17 KiB

v1.0.0-rc.6

06-16-2026

    • A truncated Grav 2.0 download can no longer be staged. PHP's HTTP stream reports end-of-file when the server, a proxy, or a flaky connection drops mid-transfer, so the kickoff's download loop exited cleanly on a partial file, and the only validation afterward was a 1KB minimum-size check — a release zip cut off at any point past that sailed through and got marked as staged. The wizard then failed at the extract step with the unhelpful Could not open zip (code 35) (libzip's "truncated zip archive"). The kickoff now cross-checks the bytes received against the response's Content-Length, verifies the file actually opens as a zip archive (with at least one entry) before the .migrating flag is written, deletes the partial file on any failure, and reports what went wrong — including how many bytes arrived versus how many were expected — along with the remedy (retry, or download the release manually and point source_local_zip at it).
    • The wizard's extract step now recognizes the libzip error codes that mean the staged zip is damaged (19 not-a-zip, 21 inconsistent, 35 truncated) and explains that the download was interrupted and how to recover (Reset Migration on the admin page, then stage again), instead of printing only the bare numeric code.
    • A configured source_local_zip is validated as a readable archive before staging, so a bad manual download is caught at kickoff with a pointer to re-download and verify it with unzip -t (the file itself is left in place, since the user supplied it).
    • Disk-full or quota errors while saving the download are now detected at write time instead of silently truncating the file.

v1.0.0-rc.5

06-10-2026

    • A customized admin path now carries across the migration. Grav 1.7 stores the admin route in admin.yaml, but Admin 2.0 reads its own admin2.yaml, so a site that changed /admin to e.g. /backend as a security measure would otherwise revert to the default after migrating. When the source route differs from the /admin default, the migration now writes it into the staged admin2.yaml (normalized to Admin 2.0's /path form), merging into any existing admin2 config rather than overwriting it. Only the route is carried — it's the one 1.7 admin setting Admin 2.0 has an equivalent for. [#6]
    • The migrate page now checks whether the user/data, user/accounts, user/config and user/env folders are reachable over the web and, on Apache, offers a one-click fix that blocks them; on other webservers it shows the exact rule to add by hand.
    • Migration now scans content for URL-based image transforms that bypass Grav's media object (e.g. image.jpg?cropResize=300,200) and turns on Grav 2.0's new system.images.url_actions toggle in the staged system.yaml when it finds any. Grav 1.7 applied these query-string actions with no gate; 2.0 disables them by default because they run with arguments an unauthenticated visitor controls. The normal developer-driven path is unaffected and deliberately left alone: a Twig/Markdown media call like page.media['x'].cropResize(300,200), or a Markdown image whose file is the page's own media (![](x.jpg?cropResize=300,200)), is resolved through the media object into a hashed cache URL with no query string and never touches the toggle. Only references Grav can't resolve to page media — absolute/rooted paths, theme:///image:// stream paths, files that aren't co-located, and anything hand-written in a theme template — keep their literal ?action= URL and need the toggle, so those are what flip it on. External and protocol-relative URLs (https://cdn.example.com/x.jpg?format=webp, //host/x.jpg?…) are skipped, since they're served by the remote host and a CDN's own query string can't be a Grav image action. The report lists the pages and templates involved, and flags any transform that requests an image above the system.images.max_pixels ceiling (still refused even with the toggle on) so you can raise the limit or rework it.

v1.0.0-rc.4

06-03-2026

    • Migration now scans the source site for Twig-in-content usage (both per-page process: twig: true and the site-wide system.yaml opt-ins) and turns on Grav 2.0's new security.twig_content gate in the migrated install, so those pages keep rendering after promote. If any Twig-enabled page also reads site config inside Twig, the config access toggle is enabled too.
    • Migration now scans Twig-enabled page content for the function and filter calls it uses and re-enables them for Grav 2.0, which tightened Twig security. Raw PHP functions (e.g. strtoupper) are added to both system.twig.safe_functions/safe_filters (so they're callable at all) and security.twig_sandbox.allowed_functions/allowed_filters (so sandboxed page content may call them); your existing safe_functions entries are preserved and merged in. Plugin-provided Twig functions (e.g. unite_gallery) are added to the sandbox allowlist and called out in the report — the providing plugin still needs to register them (ideally via the onBuildTwigSandboxPolicy event). Functions Grav 2.0 refuses outright — Utils::isDangerousFunction() (system, exec, preg_replace, …) and the sandbox's by-design exclusions (constant, read_file, evaluate, …) — are never added and are listed in the report instead. The sandbox lists are written in full (core defaults plus additions) because Grav merges them by index.
    • Step 1 pre-flight now warns when PHP's set_time_limit, ignore_user_abort, or proc_open are blocked by disable_functions (common on managed hosts such as RunCloud). With set_time_limit disabled the wizard cannot lift PHP's execution limit, so the long Step 2 copy or the GPM update of many plugins can exceed max_execution_time and die with a silent HTTP 500. The warning prints the current max_execution_time and the remedy (raise the host's max_execution_time / php-fpm request_terminate_timeout / proxy read timeout). The checks are advisory only and do not block the migration. [#5]
    • Migration now strips the dead twig.undefined_functions / undefined_filters keys from the staged system.yaml — Grav 2.0 removed the blanket undefined-function escape hatch (an unlisted function/filter is now a hard error). The retained safe_functions / safe_filters keys are preserved and merged with anything found in content. Block-style values are rewritten cleanly; a multi-line flow value the rewrite can't safely touch is flagged for you to finish by hand.
    • After turning on Grav 2.0's Twig in content security gate, the migration now also removes the matching legacy flag from the staged system.yaml so the migrated install has one setting in one place instead of two doing the same job. An explicit opt-out (pages.process.twig: false) is preserved.
    • Removing that legacy flag handles more system.yaml shapes than before: quoted values, files without a trailing newline, Windows line endings, and unrelated twig: keys under sibling blocks (which a previous version could accidentally strip too). The system.yaml rewrite is now atomic so an interrupted migration cannot corrupt the staged config. When the legacy flag is written in flow-style (e.g. process: { twig: true }) the migration cannot safely auto-remove it without losing comments, and the wizard now flags this so you can finish the cleanup by hand.
    • The source-site scanner now recognises the same quoted truthy values the per-page scanner already does, so a 1.x install with pages.process.twig: "true" correctly turns on the 2.0 security gate after migration.
    • Backup zip created on Windows is now extractable. mg_zip_webroot was passing SplFileInfo::getPathname() output straight into ZipArchive::addFile($abs, $rel) — on Windows that meant entry names were stored with native \\ separators instead of the / the zip spec requires. Every standards-tolerant extractor (7-Zip, Windows Explorer's in-place viewer, macOS Archive Utility) treated the backslashes as literal filename characters, dumping every file in the zip's root with names like user\plugins\admin\file.php and rendering directory entries as a flat breadcrumb list. Now normalized to / regardless of OS. A standalone repair script wizard/mg-repair-backup.php ships in this release for users whose pre-rc.3 Windows backup zips are still on disk — it rewrites the entry names so the zip extracts correctly with any tool.

v1.0.0-rc.3

05-13-2026

    • Pre-promote callout now warns to close any editor, git GUI (Sourcetree, GitHub Desktop, GitKraken), and terminal that has the webroot open — on Windows these processes hold file handles that block the Phase 2 delete pass.
    • Promote step on Windows now runs a pre-flight scan for locked files BEFORE deleting anything, so the wizard reports the specific paths (e.g. user/plugins/foo/.git/index) the user needs to free, rather than half-destroying the webroot and failing midway. macOS and Linux skip the scan — unlink() succeeds on open files there.
    • Promote failure callout now names the specific file that couldn't be deleted (e.g. user/plugins/foo/.git/objects/pack/pack-abc.idx) instead of just the top-level entry, so it's obvious which editor or git GUI to close.
    • Promote failure callout now includes recovery instructions for the backup zip, including a Windows-specific warning that File Explorer's in-place zip viewer renders nested paths as a flat breadcrumb list (system·src·Grav·…) and that you must use Right-click → Extract All… rather than dragging entries out.
    • README has a new Recovering from a failed promote section documenting the three-phase rollback model and platform-specific extraction commands.
    • nginx config snippet shown in Step 5 (Test) is now actually functional. The previous version put the PHP location ~ \.php$ block as a sibling of the location ^~ /grav-2/ prefix — but nginx never evaluates sibling regex locations once an ^~ prefix match wins, so PHP under the stage path was served as a static download. The snippet now nests the PHP block inside the prefix block and adds fastcgi_split_path_info and fastcgi_index to match Grav's documented nginx template. [#3]
    • Outbound HTTP from the migration wizard now honors Grav's system.http.proxy_url and system.http.proxy_cert_path settings (and the standard HTTPS_PROXY / HTTP_PROXY / ALL_PROXY env vars as fallback). Previously, every HTTP call — the Grav 2.0 zip download, GPM catalog queries, GitHub release lookups, plugin/theme replacement zips, the curated compat registry — built its own stream context with no proxy support and silently failed for sites behind a corporate proxy. Kickoff now forwards the site's proxy config into the .migrating flag at staging time, and the standalone wizard reads it via a new mg_http_context() helper. [#2]

v1.0.0-rc.2

05-06-2026

    • Step 2 compatibility breakdown now has a dedicated Will be upgraded bucket for plugins whose installed version reads as 1.7-only but for which GPM has a newer 2.0-compatible release. Previously these were rendered under Incompatible even though Phase 4's gpm update will land the new version — misleading because the user's skip/disable policy doesn't apply to them.
    • Replacement installs (admin2 + api) are now guaranteed even when the curated compatibility registry is offline or has been pruned of those entries — a hardcoded baseline maps admin → admin2 (with requires: [api]) and is merged under the remote response so any remote entry still wins per slug.
    • GPM upgrade detection no longer silently fails: getgrav.org/downloads returns the install URL under zipball_url, but the wizard was reading download. Normalized inside mg_fetch_gpm_index so every plugin with a newer 2.0-compatible release on GPM now lands in the Will be upgraded bucket and gets installed via GPM during the upgrade pass (instead of silently falling through to the GitHub fallback path).

v1.0.0-rc.1

05-04-2026

    • Two reset modes — Restart Wizard keeps the downloaded Grav 2.0 zip and lets you re-run from step 1, Reset Migration wipes everything and starts over.
    • Plugin upgrade lookups now ask GPM for the release that fits Grav 2.0 specifically, so suggested upgrades reflect what actually works on the destination.
    • Plugin upgrades during migration are offered for any plugin with a newer 2.0-compatible release on GPM, not only those in the curated compatibility registry.
    • Replacement installs (admin2, api, etc.) now fall back to the newest tagged GitHub release — including beta tags — when a plugin isn't on GPM yet.
    • Plugin updates during Copy & Migrate now run through Grav 2.0's own bin/gpm, matching how a regular admin update behaves.
    • Compatibility breakdown table groups rows by status with per-bucket counts (Compatible / Needs update / Incompatible / Will be installed) and color-coded labels for where each verdict came from.
    • Symlinked plugins and themes are preserved through the migration, so developer setups with linked plugin clones don't get clobbered.
    • Long-running steps (bulk copy, plugin upgrade) no longer time out on shared hosts with low max_execution_time.
    • The "already staged" error when starting a new migration now points at the Restart/Reset buttons instead of asking you to delete files by hand.
    • Recursive delete during reset no longer follows symlinks — protects real files outside the staged tree.
    • Plugin upgrade pass no longer clobbers plugins that are about to be replaced (admin → admin2, etc.).
    • Compatibility policy (skip/disable) now applies after the upgrade pass, so freshly upgraded 2.0-compatible plugins aren't then disabled.
    • CLI php detection handles hosts where PHP_BINARY points at php-fpm or php-cgi.

v1.0.0-beta.5

04-25-2026

    • Use 'latest' URL to always get the latest version of Grav 2.0 beta
    • Allow being run in Grav 1.7.49+

v1.0.0-beta.4

04-21-2026

    • Default source URL now points at the released Grav 2.0 beta grav-update package (https://getgrav.org/download/core/grav-update/2.0.0-beta.1?testing) instead of a local dev zip. The update package ships system/vendor/bin only (no baseline user/ pages) — this avoids polluting migrated sites with default home/typography pages that the full install package would otherwise drop on top of the source content.
    • Staging flow reworked around a single bulk copy: Step 2 now copies the entire source user/ directory verbatim into the staged install (including any custom folders beyond plugins/themes/accounts), then applies plugin compat policy, auto-updates, and replacement installs in place. Step 3 becomes a transform-only step that rewrites admin.*api.* on the already-copied account yamls. Step 4 is a confirmation/summary of what landed in staged user/.
    • Staged layout is now package-agnostic. After extract, user/, user/{plugins,themes,accounts,config,data,pages}/, and a root .htaccess (materialized from webserver-configs/htaccess.txt) are created when missing, so downstream steps work whether the source zip is grav-update, grav, or grav-admin.
    • Theme handling and messaging: themes are always kept as-is (skip policy no longer removes them); incompatible themes render as ⚠ "Kept — Twig 3 compatibility enabled (verify before promoting)" rather than a scary ✗. Step 2 intro and stream subtitles explain the Twig 3 compat layer and that custom/unmarked themes are expected to work through it.
    • Top-level user/ dotfiles (.git, .DS_Store, editor backups) and symlinks are explicitly excluded from the bulk copy and recorded in the step summary.
    • do_plugins_themes and do_content no longer abort with "Source or staged user/ missing" when the source package is grav-update (which ships no user/). Extract normalizes the skeleton first.
    • mg_patch_staged_htaccess (used by the Test step to set RewriteBase for sub-path testing) no longer fails on grav-update-based stages — the extract step materializes .htaccess from the zip's webserver-configs/htaccess.txt template when missing.

v1.0.0-beta.3

04-20-2026

    • Use beta release URL of Grav 2.0

v1.0.0-beta.2

04-16-2026

    • Preserve executable bits on bin/* during staged zip extract. The raw fwrite()-based extractor dropped the mode stored in the zip's central directory, landing bin/grav, bin/gpm, bin/plugin, and bin/composer.phar at 0644 post-migration and breaking CLI tooling on the fresh 2.0 install. Extract now honors the zip's unix mode when present, with a safety-net chmod 0755 for anything directly under bin/ so test-built zips (which omit mode metadata) also work.

v1.0.0-beta.1

04-15-2026

    • Initial scaffold: kickoff plugin for staging Grav 2.0 alongside an existing 1.7/1.8 site.
    • CLI: bin/plugin migrate-grav init and bin/plugin migrate-grav status.
    • Admin page with single-click staging that redirects to the standalone wizard.