Grav 1.7 introduces a few new features, improvements, bug fixes and provides many architectural changes which pave the road towards Grav 2.0. Here are a few highlights:
webp image format, lazy loading and more.{% cache %} tag and improved performance especially in admin.IMPORTANT: For most people, Grav 1.7 should be a simple upgrade without any issues, but like any upgrade, it is recommended to take a backup of your site and test the upgrade in a testing environment before upgrading your live site.
This behavior is a result of the new default of auto-escaping being true in Grav 1.7. This is a security enhancement, and if you are upgrading from a version prior to 1.7, we automatically enable Twig Compatibility setting in the system configuration to ensure your old Twig code will continue to function. If you manually update to 1.7 or upgrade in any way that does not go through the GPM self upgrade process, you should set this setting yourself.
Check out the Twig section of this guide for full details...
As we have upgraded to a newer version of Symfony framework, the YAML parser is stricter than it was in versions prior to 1.7. To handle this we have included an older version of the parser that is available when enabling Yaml Compatibility. This is automatically handled for you if you upgrade to Grav 1.7 via GPM, but if you have upgraded manually, you will need to set this value yourself.
Check out the YAML section of this guide for full details...
If your admin is displaying with untranslated strings in the interface, it's most likely because you have previously disabled Language Translations. This was buggy in previous versions of Grav and disabling it, didn't actually disable translations throughout the admin as intended. This is fixed in Grav 1.7 and this setting is doing what it is intended to do, show the translation codes in uppercase rather than the translated strings themselves.
Check out the Troubleshooting section for the fix.
In Grav 1.7 we introduced Flex Pages as the new default page management UI. Also, to optimize performance, we stopped initializing pages on every admin call. Switching back to regular Grav Pages might temporarily resolve your issue. This is done by editing the FlexObjects plugin and disabling Pages (Admin).
To properly address the issue, custom plugins should be updated to support both Grav Pages and Flex Pages by using PageInterface and also should explicitly Pages when required.
Check out the Pages section and Admin Section of this guide for full details...
There have also been some specific plugin issues that have already been discovered. Check out the Troubleshooting section of this page for specific issues with plugins.
Grav 1.7.8 adds support for defining any blueprint in your theme. This means that if you have page blueprints in blueprints/pages/ folder, standard blueprint locations are used, just like in plugins. Unfortunately some older themes may have a mix of files in blueprints/ and blueprint/pages, which breaks the detection and causes either missing fields in admin when editing the pages or a fatal error: Loop detected while extending blueprint file.
If either of these errors happen, check out the Troubleshooting section for the fix.
Grav 1.7 requires PHP 7.3.6 or later version. The recommended version is the latest PHP 7.4 release.
IMPORTANT: Grav 1.7 YAML parser is more strict and your site may break if you have syntax errors in your configuration files or page headers. However, if you update your existing site using bin/gpm or Admin Plugin upgrade process keeps most of the broken YAML syntax working.
To revert to the old behavior you need to make sure you have following setting in user/config/system.yaml:
strict_mode:
yaml_compat: true
or in Admin under Configuration → Advanced -> YAML Compatibility

TIP: Grav 1.6 Upgrade Guide has a dedicated YAML Parsing section to help you to fix these issues.
By default, Grav 1.7 uses a Symfony 4.4 YAML parser, which follows the YAML standard specification more closely than the older versions of Grav. This means that YAML files which previously worked just fine, may cause errors resulting from being invalid YAML. However, Grav will by default still fall back to the older 3.4 version of the parser to keep your site up and running.
TIP: You should run CLI command bin/grav yamllinter or visit in Admin > Tools > Reports before and after upgrade and fix all the YAML related warnings and errors.
IMPORTANT: Grav 1.7 enables Twig Auto-Escaping by default. However, if you update your existing site using bin/gpm or Admin Plugin upgrade process keeps the existing auto-escape settings.
To revert to the old behavior you need to make sure you have following settings in user/config/system.yaml:
twig:
autoescape: false
strict_mode:
twig_compat: true
or in Admin under Configuration → Advanced -> Twig Compatibility
And please remember to clear cache after doing this!

TIP: Grav 1.6 Upgrade Guide has a dedicated Twig section. It is very important to read it first!
Twig template engine has been updated to version 1.43, but it also supports Twig 2.13. In order to support this newer version of Twig, you need to update any old syntax in your Twig templates. Grav 1.6 Upgrade Guide helps you to do this.
Additional changes in templating are:
{% cache %} Twig tag eliminating need for twigcache extension.array_diff() twig functiontemplate_from_string() twig functionsvg_image() twig function to make it easier to 'include' SVG source in Twigurl() twig function to take third parameter (true) to return URL on non-existing file instead of returning false|array twig filter to work with iterators and objects with toArray() methodauthorize() twig function to work better with nested rule parameters|yaml_serialize twig filter: added support for JsonSerializable objects and other array-like objectsexternal.html.twig, default.html.twig, and modular.html.twig{% script 'file.js' at 'bottom' %} instead of in 'bottom' which is brokenIMPORTANT: Grav 1.7 changes the behavior of Strict Validation. However, if you update your existing site using bin/gpm or Admin Plugin upgrade process keeps the existing strict mode behaviour.
Strict mode Improvements: Inside forms, declaring validation: strict was not as strict as we hoped because of a bug. The strict mode should prevent forms from sending any extra fields and this was fixed into Grav 1.7. Unfortunately many of the old forms declared to be strict even if they had extra data in them.
To revert to the old behavior you need to make sure you have following setting in user/config/system.yaml:
strict_mode:
blueprint_compat: true
XSS Injection Detection is now enabled in all the frontend forms by default. Check the documentation on how to disable or customize the checks per form and per field.
Because of this, we added new configuration option system.strict_mode.blueprint_compat: true to maintain old validation: strict behavior. It is recommended to turn off this setting to improve site security, but before doing that, please search through all your forms if you were using validation: strict feature. If you were, either remove the line or test if the form still works.
NOTE: This backwards compatibility fallback mechanism will be removed in Grav 2.0
Important: Grav 1.7 moves environments into user://env folder. The old location still works, but it is better to move environments into a single location future features may rely on it.
Grav 1.7 also adds support for Server Based Environment Configuration and Server Based Multi-Site Configuration. This feature comes handy if you want to use for example docker containers and you want to make them independent of the domain you happen to use. Or if do not want to store secrets in the configuration, but to store them in your server setup.
In addition setup.php file can now be in either GRAV_ROOT/setup.php or GRAV_ROOT/GRAV_USER_PATH/setup.php. The second location makes it easier to use environments with git repositories containing only user folder.
Admin has now new Accounts Administration using Flex Users:
NOTE: Flex Users feature is not yet used in the frontend of your site.
The existing Pages Administration has been greatly improved with Flex Pages:
BACKWARDS COMPATIBILITY BREAK: We fixed 404 error page when you go to non-routable page with routable, visible child pages under it. Now you get redirected to the first routable, visible child page instead. This is probably what you wanted in the first place.
NOTE: Flex Pages feature is not yet used in the frontend of your site.
Grav 1.7 changed the behavior of how the multi-language fallbacks work for the pages.
Previously if the page did not exist with the requested language, the old implementation looked up the next supported language. This meant that the untranslated page was always displayed, but the page could be using some unknown language to the reader.
The new behavior is to fall back only to the default language of the site. This default behavior can be overridden by setting fallback languages per language by using system.languages.content_fallback configuration option.
If the page does not exist in any of the fallback languages, 404 Not Found will be displayed instead.
BACKWARDS COMPATIBILITY BREAK: Please add correct fallback languages for the page content in system.yaml or admin: Configuration > System > Languages > Content Language Fallback.
Media handling has been greatly improved in Grav 1.7. Some highlights are:
webp image formatloading=lazy attributes on images. Can be set in system.images.defaults or per md image with ?loading=lazynoprocess specific items only in Link/Image Excerpts, e.g. http://foo.com/page?id=foo&target=_blank&noprocess=idSome highlights are:
--env and --lang parameters to set the environment and the used language respectively (-e does not work anymore)bin/grav server CLI command to easily run Symfony or PHP built-in web serversScheduler cron command check and more useful CLI information-r <job-id> option for Scheduler CLI command to force-run a jobbin/grav yamllinter CLI command by adding an option to find YAML Linting issues from the whole site or custom folderAdded new configuration option to keep default language in .md files if set to false
languages.include_default_lang_file_extension: true|falseAdded new configuration option to set fallback content languages individually for every language
languages.content_fallback: See Language ConfigurationAdded new configuration option to choose between debugbar and clockwork
debugger.provider: clockwork|debugbarAdded new configuration option to hide potentially sensitive information
debugger.censored: false|trueAdded new configuration option to maintain old validation: strict behavior
strict_mode.blueprint_compat: true|falseAdded system configuration support for HTTP_X_FORWARDED headers (host disabled by default)
http_x_forwarded.protocol: true|falsehttp_x_forwarded.host: true|falsehttp_x_forwarded.port: true|falsehttp_x_forwarded.ip: true|falseAdded new configuration option security.sanitize_svg to remove potentially dangerous code from SVG files
sanitize_svg: true|falseUpgraded bin/composer.phar to 2.0.2 which is all new and much faster
Please add composer.json file to your plugin and run composer update --no-dev (and remember to keep it updated):
composer.json
{
"name": "getgrav/grav-plugin-example",
"type": "grav-plugin",
"description": "Example plugin for Grav CMS",
"keywords": ["example", "plugin"],
"homepage": "https://github.com/getgrav/grav-plugin-example",
"license": "MIT",
"authors": [
{
"name": "...",
"email": "...",
"homepage": "...",
"role": "Developer"
}
],
"support": {
"issues": "https://github.com/getgrav/grav-plugin-example/issues",
"docs": "https://github.com/getgrav/grav-plugin-example/blob/master/README.md"
},
"require": {
"php": ">=7.1.3"
},
"autoload": {
"psr-4": {
"Grav\\Plugin\\Example\\": "classes/",
"Grav\\Plugin\\Console\\": "cli/"
},
"classmap": [
"example.php"
]
},
"config": {
"platform": {
"php": "7.1.3"
}
}
}
See Composer schema
Please use autoloader instead of require in the code:
example.php
/**
* @return array
*/
public static function getSubscribedEvents(): array
{
return [
'onPluginsInitialized' => [
// This is only required in Grav 1.6. Grav 1.7 automatically calls $plugin->autolaod() method.
['autoload', 100000],
]
];
}
/**
* Composer autoload.
*
* @return \Composer\Autoload\ClassLoader
*/
public function autoload(): \Composer\Autoload\ClassLoader
{
return require __DIR__ . '/vendor/autoload.php';
}
Plugins & Themes: Call $plugin->autoload() and $theme->autoload() automatically when object gets initialized
Make sure your code does not use require or include for loading classes
blueprints.yaml)Please add:
slug: folder-name
type: plugin|theme
Make sure you update your dependencies. I recommend setting Grav to either 1.6 or 1.7 and update your code/vendor to PHP 7.1
dependencies:
- { name: grav, version: '>=1.6.0' }
Added themes to cached blueprints and configuration
Grav 1.7.8 adds support for defining any blueprint in your theme. Move all files and folders in blueprints/ into blueprints/pages/ to keep your theme forward compatible. Also remember to update minimum Grav dependency to >=1.7.8.
Session::regenerateId() method to properly prevent session fixation issuesuser.authorize() now requires user to be authorized (passed 2FA check), unless the rule contains login in its name.
Added support for more advanced ACL (CRUD)
BC BREAK user.authorize() and Flex object.isAuthorized() now have two deny states: false and null.
Make sure you do not have strict checks against false: $user->authorize($action) === false (PHP) or user.authorize(action) is same as(false) (Twig).
For the negative checks you should be using !user->authorize($action) (PHP) or not user.authorize(action) (Twig).
The change has been done to allow strong deny rules by chaining the actions if previous ones do not match: user.authorize(action1) ?? user.authorize(action2) ?? user.authorize(action3).
Note that Twig function authorize() will still keeps the old behavior!
Added default templates for external.html.twig, default.html.twig, and modular.html.twig
Admin uses Flex Pages by default (can be disabled from Flex-Objects plugin)

Added page specific admin permissions support for Flex Pages
Added root page support for Flex Pages
Fixed wrong Pages::dispatch() calls (with redirect) when we really meant to call Pages::find()
Added Pages::getCollection() method
Moved collection() and evaluate() logic from Page class into Pages class
DEPRECATED $page->modular() in favor of $page->isModule()
DEPRECATED PageCollectionInterface::nonModular() in favor of PageCollectionInterface::pages()
DEPRECATED PageCollectionInterface::modular() in favor of PageCollectionInterface::modules()
BC BREAK Fixed Page::modular() and Page::modularTwig() returning null for folders and other non-initialized pages. Should not affect your code unless you were checking against false or null.
BC BREAK Always use \Grav\Common\Page\Interfaces\PageInterface instead of \Grav\Common\Page\Page in method signatures
Admin now uses Flex Pages by default, collection will behave in slightly different way
BC BREAK $page->topParent() may return page itself instead of null
BC BREAK $page->header() may now \Grav\Common\Page\Header return object instead of stdClass, you need to handle both (Flex vs regular)
MediaTrait::freeMedia() method to free media (and memory)Media by using PSR-7Getters, accessing $media->$filename no longer works, use $media[$filename] instead!Excerpts::processLinkHtml() methodFlex Users in the frontend (not recommended to use yet)Flex Users by default (can be disabled from Flex-Objects plugin)Flex Users: obey blueprints and allow Flex to be used in admin onlyFlex Users: user and group ACL now supports deny permissionsUserInterface::authorize() to return null having the same meaning as false if access is denied because of no matching rule\Grav\Common\User\Group in favor of $grav['user_groups'], which contains Flex UserGroup collection\Grav\Common\User\Interfaces\UserInterface instead of \Grav\Common\User\User in method signaturesFramework Flex classes directly, it's better to use or extend classes under Grav\Common\Flex\Types\Generic namespace$grav['flex'] to access all registered Flex Directories
FlexRegisterEvent which triggers when $grav['flex'] is being accessed the first timehasFlexFeature() method to test if FlexObject or FlexCollection implements a given featuregetFlexFeatures() method to return all features that FlexObject or FlexCollection implementsFlexObject::refresh() method to make sure object is up to dateFlexStorage::getMetaData() to get updated object meta information for listed keysFlexDirectoryInterface interfacesame_as to Flex ObjectsFlex Pages method $page->header() returns \Grav\Common\Page\Header object, old Page class still returns stdClassPageCollectionInterface::nonModular() into PageCollectionInterface::pages() and deprecated the old methodPageCollectionInterface::modular() into PageCollectionInterface::modules() and deprecated the old methodFlexDirectory::getObject() can now be called without any parameters to create a new objectFlexDirectory::update() and FlexDirectory::remove()Grav\Common\FlexFlexStorageInterface::getStoragePath() and getMediaPath() can now return nullFlexStorageInterface::getMetaData()edit_list.html.twig file to a form field in order to customize look in the listing viewRoute classLanguage::getPageExtensions() to get full list of supported page language extensionsLanguage::getFallbackPageExtensions() to fall back only to default language instead of going through all languagesuser/env folderSerializable methods are now final and cannot be overridden.flatten_array filter to form field validationsecurity@: or: [admin.super, admin.pages] in blueprints (nested AND/OR mode support)validate: value_type: bool|int|float|string|trim to array to filter all the values inside the arrayIf your plugins has blueprints folder, initializing it in the event will be too late. Do this instead:
class MyPlugin extends Plugin
{
/** @var array */
public $features = [
'blueprints' => 0, // Use priority 0
];
}
Symfony EventDispatcher directly instead of rockettheme/toolbox wrapper.$grav->dispatchEvent() method for PSR-14 eventsPluginsLoadedEvent which triggers after plugins have been loaded but not yet initializedSessionStartEvent which triggers when session is startedFlexRegisterEvent which triggers when $grav['flex'] is being accessed the first timePermissionsRegisterEvent which triggers when $grav['permissions'] is being accessed the first timeonAfterCacheClear eventCheck onAdminTwigTemplatePaths event, it should NOT be:
public function onAdminTwigTemplatePaths($event)
{
// This code breaks all the other plugins in admin, including Flex Objects
$event['paths'] = [__DIR__ . '/admin/themes/grav/templates'];
}
but:
public function onAdminTwigTemplatePaths($event)
{
// Add plugin template path for admin.
$paths = $event['paths'];
$paths[] = __DIR__ . '/admin/themes/grav/templates';
$event['paths'] = $paths;
}
3.5.1Utils::functionExists(): PHP 8 compatible function_exists()Utils::isAssoc() and Utils::isNegative() helper methodsUtils::simpleTemplate() method for very simple variable templatingUtils::fullPath() to get the full path to a file be it stream, relative, etc.CSVFormatter::decode()Security::sanitizeSVG() function$grav->close() method to properly terminate the request with a responseFolder::countChildren() method to determine if a folder has child foldersFileRoute::getBase() methodRoute objects immutable. This means that you need to do: {% set route = route.withExtension('.html') %} (for all withX methods) to keep the updated version.Content-Encoding handling in Apache when content compression is disabledUri::getAllHeaders() compatibility functionJsonFormatter options to be passed as a stringserve() method:$this->setLanguage($langCode); before doing anything else if you want to set the language (or use default)$this->initializeGrav(); Already called if you're in bin/plugin, otherwise you may need to call this one$this->initializePlugins(); This initializes grav, plugins (up to onPluginsInitialized)$this->initializeThemes(); This initializes grav, plugins and theme$this->initializePages(); This initializes grav, plugins, theme and everything needed by pagesbin/grav yamllinter to find any YAML parsing errors in your site (including your plugins and themes).Added Content Editor option to user account blueprint
BC BREAK Admin will not initialize frontend pages anymore, this has been done to greatly speed up Admin plugin.
Please call $grav['admin']->enablePages() or {% do admin.enablePages() %} if you need to access frontend pages. This call can be safely made multiple times.
If you're using Flex Pages, please use Flex Directory instead, it will make your code so much faster.
Admin now uses Flex for editing Accounts and Pages. If your plugin hooks into either of those, please make sure they still work.
Admin cache is enabled by default, make sure your plugin clears cache when needed. Please avoid clearing all cache!
init() method, classes without it will stop working in the future.ERROR: flex-objects.html.twig template not found for pageIf you get this error after upgrading to Grav 1.7, it might be related to a plugin called content-edit. If you disable this plugin, the error should resolve itself. Grav Issue #3169
If your admin plugin looks like this:

The fix is very easy, and can be done even when not fully translated. Simply navigate to PLUGIN_ADMIN.CONFIGURATION and then in PLUGIN_ADMIN.LANGUAGES, set PLUGIN_ADMIN.LANGUAGE_TRANLATIONS to PLUGIN_ADMIN.YES:

If you cannot see your custom fields when editing the page, your theme is using two conflicting locations for page blueprints.
If the theme was not created by you, please report a bug to the theme author.
To fix the bug, you need to move all files and folders in your theme from blueprints/ to blueprints/pages/ (requires Grav 1.7.8+). Alternatively if the theme must support older versions of Grav, do the opposite.
The easiest fix for loop error is to move the files into their proper location, please see the above issue.
Alternatively you can fix the issue by changing the broken page blueprint from:
extends@:
type: [NAME]
context: 'blueprints://pages'
where [NAME] is the filename (without the file extension) of the blueprint itself, to:
extends@: self@
It has been reported that after upgrading to latest Grav 1.7 and Admin 1.10, some admin pages appear broken and not fully styled. THis could be related to the imagecreate plugin. Disabling this plugin is not enough, you must completely remove the plugin, and then the error should resolve itself. Admin Issue #2035
While we recommend resolving any issues you may have to ensure that Grav 1.7 and future updates will be an easy upgrade, there are going to be scenarios where you have custom plugin functionality, or don't have the developer resources handy, and just need to get back to Grav 1.6 quickly.
If you have CLI access to the site, this can be done by running these commands from the root of your Grav 1.7 site:
wget -q https://getgrav.org/download/core/grav-update/1.6.31 -O tmp/grav-update-v1.6.31.zip
wget -q https://getgrav.org/download/plugins/admin/1.9.19 -O tmp/grav-plugin-admin-v1.9.19.zip
unzip tmp/grav-update-v1.6.31.zip -d tmp
unzip tmp/grav-plugin-admin-v1.9.19.zip -d tmp
cp -rf tmp/getgrav-grav-plugin-admin-5d86394/* user/plugins/admin/
cp -rf tmp/grav-update/* ./
Basically it does a direct-install of the latest version of Grav 1.6 and Admin 1.9 on top of your current installation. It doesn't touch the user/ folder so your content and plugins are not impacted.
For those who do not have CLI access, download grav-update-v1.6.31.zip and grav-plugin-admin-1.9.19.zip files using the links given here. Unzip the files into your filesystem. Then use your favorite FTP/SFTP client to copy all the Grav files to your WEBROOT and Admin files into WEBROOT/user/plugins/admin.
Found errors? Think you can improve this documentation? Simply click the Edit link at the top of the page, and then the icon on Github to make your changes.
Powered by Grav + with by Trilby Media.