Reads the new `next_major` block from grav.json (sent by a family-aware
resources server when a 1.x client is served a 1.x release alongside a 2.x
hint) and exposes it via Upgrader::isNextMajorAvailable(),
getNextMajorVersion(), getMigrationUrl(). The admin plugin uses these to
show a dashboard migration banner without implying an automatic upgrade.
isNextMajorAvailable() no longer compares raw remote vs local majors —
under family-aware serving, the remote version is already the client's
family, so the old logic would silently stop firing. It now requires
the server hint and verifies it truly points to a newer major.
Tests updated to inject the hint via an extended TestableUpgrader.
- Delete InstallSnapshotManifestTest: references isSnapshotManifest()
which no longer exists after safe-upgrade removal
- Fix InstallCompatibilityTest: GRAV_ROOT is a PHP constant that cannot
be redefined per-test; add optional $root param to detectIncompatiblePackages,
isPluginEnabled, and isThemeEnabled so tests can pass a tmpDir directly
- Remove SafeUpgradeService, RollbackCommand, SafeUpgradeRunCommand, and bin/restore
- Simplify Install.php: remove all snapshot logic; keep forceSafeUpgrade() as no-op stub
for backward compat with pre-2.0 upgrade scripts that call it in finally blocks
- Simplify SelfupgradeCommand: remove --safe/--legacy flags, safe-upgrade window tracking,
and manifest block; keep RecoveryManager plugin-disable calls
- Simplify PreflightCommand: use Install::instance()->generatePreflightReport() directly
- Simplify recovery.php: remove auth, rollback, and quarantine sections; keep
clear-flag and disable-recovery actions
- Upgrader::isUpgradable() now returns false when remote is a different major.minor family
(e.g. 1.8.x will not auto-upgrade to 2.0.x)
- Add Upgrader::isNextMajorAvailable() for future informational notices
- Add bin/gpm-cache-inject helper for local E2E testing without a live server
- Add UpgraderFamilyTest covering all cross-family and same-family scenarios (10/10)
Extends the 1.7/1.8 compatibility machinery to treat a grav >= 2.0
dependency as ['2.0']. SelfupgradeCommand's preflight already keys off
the target package's major.minor, so upgrading 1.8 → 2.0 now correctly
flags plugins that only declare 1.7 or 1.8 as blocking.
- GPM/Local/Package::inferCompatibility(): grav >= 2.0 → ['2.0']
- Installer/Install::inferCompatibleVersions(): grav >= 2.0 → ['2.0']
- IndexCommand / InfoCommand / UpdateCommand: magenta '2.0' badge
Ships as part of the final 1.8 beta so the installed base has a clean
upgrade gate when users run `bin/gpm selfupgrade` against a Grav 2.0
release. The actual migration wizard is a 2.0-only deliverable and is
NOT backported here.
Adds a new `compatibility:` field to plugin/theme blueprints.yaml that allows
authors to declare which Grav versions they've tested on:
compatibility:
grav: ['1.7', '1.8']
api: ['1.0']
When absent, compatibility is inferred from dependencies (grav >=1.8 means
1.8-only, otherwise assumes 1.7). This gates major upgrades (1.7→1.8) by
blocking if enabled plugins aren't marked as 1.8-compatible.
Core changes:
- Install.php: detectIncompatiblePackages() in preflight checks
- SafeUpgradeService: same detection for admin pre-download checks
- SelfupgradeCommand: interactive handling (disable/continue/abort)
- GPM Local/Package: computed compatibility property from blueprints
- CLI badges: IndexCommand, InfoCommand, UpdateCommand show 1.7/1.8 badges
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Registers a new media:// stream pointing to user://media, providing
a dedicated writable stream for site-wide media files not tied to
specific pages. Supports all file types (not just images like the
existing image:// stream).
- Replace empty media.yaml blueprint stub with full form fields
- Add PLUGIN_ADMIN media translation keys (MEDIA, MEDIA_TYPES, FILE_EXTENSION,
TYPE, THUMB, MIME_TYPE, IMAGE_OPTIONS) to all 44 core language files
- Enables media configuration without the admin plugin being enabled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `schedule` field in backup profile configuration was never checked
when registering backup jobs with the scheduler. This meant backups
would always run on schedule even when `schedule: false` was set.
Fixes#4039
- Fix YamlUpdater.undefine() to actually remove lines from YAML files
- Add maintenance mode (503) during core upgrades via .upgrading flag
- Add opcache_reset in sophisticatedInstall() for reliable file operations
- Add early PHP 8.3 version check in bin/grav
- Add PHP CLI vs web version mismatch hint in Install error messages
- Update 1.7 bridge version references from 1.7.50 to 1.7.51
- Disable recovery_mode by default until stability confirmed
- Add standalone upgrade.php script (CLI + web)
- Update markdowndocs to ^3.0
The original CVE-2023-31506 fix missed the deprecated <isindex> HTML tag,
which can still be used for XSS via event handlers like onmouseover.
The <isindex> tag is deprecated in HTML5 and has no legitimate modern use.
Security fix for IDOR-style information disclosure where the admin
email address was leaked in the <title> tag even on 403 Forbidden
responses.
The edit view title template previously included the email:
{{ fullname ?? username }} <{{ email }}>
Now shows only the name/username without email:
{{ fullname ?? username }}
This prevents low-privilege users from enumerating admin email
addresses by accessing /admin/accounts/users/{username} URLs.