Downloads
Everything Grav
Download Grav
Grav Core is the base package with core functionality and a few essential starting pages. Grav Core + Admin also includes the Administration Panel plugin. Both are easy to get started with — check out our Basic Tutorial and Guide to the Administration Panel.
Latest stable release
Production-ready. The version we recommend for every new site and every upgrade of an existing one.
Get Started
1
Quick installation
- Download either the Grav Core or Grav Core + Admin2 plugin installation package.
- Extract the zip file into your webroot.
- Point your browser at your local webserver:
http://yoursite.com
2
How to install the Admin2 plugin
If you have not already installed the admin plugin, you can do so easily with GPM:
$
This will install the admin plugin plus its dependencies (api & login). After this is complete, point your browser to your Grav installation and you will be prompted to create a new admin user.
Changelog
v2.0.24
Latest
2 days ago
-
- A dependency can now name the generation of Grav it is for. A plugin that supports both 1.7 and 2.0 often needs a different version of the same dependency on each, so a
dependenciesentry takes an optionalgravkey:- { name: form, version: '>=9.1.0', grav: '2.0' }. Entries without it apply everywhere, so existing blueprints are unchanged. See Plugin Compatibility - A new
system.images.progressive_jpegsetting, on by default, controls whether resized and cached JPEGs are saved as progressive
- A dependency can now name the generation of Grav it is for. A plugin that supports both 1.7 and 2.0 often needs a different version of the same dependency on each, so a
-
- Installing a package whose dependency is not in the GPM index now says so and carries on, instead of stopping the command with a PHP fatal error. A plugin that still asks for the Grav 1.7 admin plugin was enough to trigger it getgrav/grav-premium-issues#618
- A plugin that asks for the
adminplugin now has that read as Admin 2 on Grav 2, so a plugin written for both 1.7 and 2.0 installs instead of failing on a dependency that cannot exist there. The version it asks for is not carried over, because it describes the old admin's numbering getgrav/grav-premium-issues#618 - Any other dependency that cannot be installed on this generation of Grav is now left out of the install rather than attempted and failed
- A JSON request whose body is a bare scalar (
"text",12345,true) sent withContent-Type: application/jsonno longer answers a 500 from the request pipeline before any route runs. It is treated as an empty body, so a plugin's webhook or API route gets to answer it, log it and refuse it itself. Witherrors.displayon, the old failure also printed a stack trace with server paths to whoever sent it - An image default such as
resizeset insystem.images.defaultsworks again. Every image manipulation was being skipped since 2.0.22, leaving only the loading and decoding hints #4282 - Settings such as
loading: lazynow stay on the image whenlinkis also on, instead of moving onto the surrounding link where the browser never sees them #4282 - Resized and cached JPEGs are saved as progressive again, so a photo appears as a whole blurry image that sharpens instead of filling in one line at a time. It has been Grav's default since 2014 but silently stopped working in 1.4.6 #4284
- A media file's own settings from
media.yamlare no longer overwritten the moment the image is opened, so custom default filters for an image type work again - A URL typed with a trailing slash no longer errors out on a site that has the debugger switched on and the Login plugin protecting page media, because the debug bar is now skipped on redirects, where there is no page to put it on #4280
- With the optional
system.session.read_and_closesetting turned on, a change made to the session early in a request is no longer thrown away by a later write in that same request, and a message added just before a redirect now reaches the page it was meant for #4281
v2.0.23 4 days ago
-
composer.jsonnow declares thectypeandsessionextensions it has always used, and suggestsfileinfoandsimplexml. Installing withcomposer create-projecton a machine missing one of these no longer quietly walks back to a years-old release of Grav instead of failing #4273
-
- Reading the browser name, platform or version no longer raises a PHP deprecation notice when a request arrives with no user agent, which is every request from a bare script or a health check
- Opening Clockwork before anything has been profiled, on a fresh install or right after
bin/grav clear, now reports that there is no data yet instead of failing with a 500 - A
GRAV_CONFIG__override set totrueorfalsenow reaches the configuration as a real yes/no value instead of the word itself, so switching something off from a.envfile or the server environment actually switches it off. Thanks to @nerdyjan for the report and @AdilAzhariOmsan for the fix #4277 Uri::ip()now reads the visitor's address from$_SERVER, falling back to the environment, so hosts that don't hand request variables to PHP's environment no longer report every visitor asUNKNOWN. Anything that counts per address there, such as the Login plugin's failed-login lockout and per-IP rate limiting, had been sharing a single bucket. Thanks to @sandymac #2507- On those same hosts the
system.http_x_forwardedoptions forip,client_ipandcf_connecting_iphad no effect at all, and now work as documented. If you turned one on and saw nothing change, turn it back off unless the site really is behind a proxy that overwrites that header
v2.0.22 6 days ago
-
- The
url()Twig function now takes a language, so a link to a route that isn't a page - a search page, a form action - can carry the site's language prefix:{{ url('/search', lang=true) }} - Blueprints can use a
mediafield type, which saves a picked file as its path and keeps a list of them when the field allows more than one - Page collections can now exclude one or more template types with
notOfType(), the counterpart to the existingofType()#3910 - The scheduler can now run the jobs that have missed their scheduled time, rather than only the ones due this very minute. Run it with
bin/grav scheduler --catch-up, which is what you want on a site that has no cron entry set up
- The
-
- Twig in page content can no longer read the site's configuration through the
print_r,vardump,json_encode,yaml_encodeandstringfilters. The check that was meant to stop it had been asking whether the whole site was in sandbox mode, which Grav never does — it decides per template — so it had been letting everything through. Thanks to @Vectrain51 - The
|map,|filterand|reduceTwig filters now check for themselves whether the template calling them is sandboxed, rather than relying on Twig to work it out. Thanks to @DhiyaneshGeek - The scheduler no longer rebuilds a queued job from an unsigned queue file, so a file written into the queue folder by something other than Grav can at most re-run a job the site was already set up to run. The page index also refuses to build objects while reading its cache. Thanks to @elite0529
- Plugin and theme descriptions are now rendered in Parsedown's safe mode before admin displays them, so a description carrying raw HTML shows as text rather than being rendered. Thanks to @alham-rizvi
- The debugger's Clockwork data endpoint now answers only requests coming from the server itself, or requests presenting the secret set in the new
debugger.tokenoption. Cookies and API tokens are no longer recorded in profiler data whatever thecensoredoption is set to - Building a URL is now faster, which adds up over the hundreds of asset and link URLs a single page render produces
- The scheduler now records whether a run was started by cron or by hand, and a run you started yourself no longer counts as evidence that cron is set up
bin/grav scheduler -rnow records the run against each job, the same as a scheduled run, so the next run knows what has already happened- A field that is rejected only for being too long or too short now says so, and gives both the length submitted and the limit, instead of the same "Invalid input" any other bad value gets
- Multiline fields no longer carry a length limit low enough to affect real writing. Set
max: 0on a field to remove the limit altogether
- Twig in page content can no longer read the site's configuration through the
-
- The
|reduceTwig filter now actually reduces. It was running the|mapcode by mistake and throwing away the starting value, so[1,2,3]|reduce((c, v) => c + v, 0)gave back a list instead of6. Thanks to @DhiyaneshGeek - A form field's
minlengthandmaxlengthare now checked when the form is submitted, not only by the browser. They were being written into the page as HTML attributes but ignored on the server, so anything that skipped the browser's own check went straight through #642 - A field with a
stepset now accepts the lengths and counts that land on a step, and rejects the ones that do not. The check was the wrong way round, so it rejected exactly the values it was meant to allow - A long page can be saved from the admin again. Page content was capped at 65,536 characters, so anything longer than roughly twenty pages of text was refused, and the only way to edit it was to write the file directly #3643
- A site installed in a subfolder no longer mangles URLs whose path repeats the install folder's name, such as an image at
/images/subdir/photo.jpgon a site installed at/subdir - A link to a page that carries a query string or an anchor, such as
/blog?page=2, now resolves to the page and keeps its language prefix, instead of being passed through as a plain path - On a site installed in a subfolder, links written with the full path now resolve to the page, so they pick up the site's language and page extension
- A cache folder that the web server cannot write to no longer takes the whole site down. Grav now logs a warning naming the folder and serves the request without the cache, so the front end and the admin both stay reachable and the Problems plugin can report what is wrong #4260
- The same failure writing
user/config/versions.yamlno longer stops the site either #3688 - Errors about a file that cannot be written now name the folder and say whether it is missing or not writable, instead of only reporting the file
- A session cookie name starting with
__Secure-or__Host-now keeps that prefix and is sent with the settings browsers require for it, so the extra protection those prefixes give actually applies. Thanks to @wakqasahmed for the fix #3773 Note: sites whosesystem.session.namecontains capitals, underscores or a leading or trailing dash will get a slightly different cookie name after this update, which signs their users out once. - The scheduler no longer reports that cron is not set up when the crontab entry is written in a valid but slightly different style, such as one using
&&or an absolute path tobin/grav - On a site with a custom scheduler job of its own, looking up a job by name no longer misses every job the system and its plugins register, so the backup and cache jobs can be found and run individually
- A scheduler job that finished its work but could not then write its output file, send its notification email or run its callback no longer aborts the whole run. The remaining jobs run, every result is still recorded, and the problem is written to the log
- A scheduler job registered without a schedule of its own no longer causes an error when its next run time is worked out
bin/grav scheduler -jno longer fails on a site with jobs registered by a plugin, and-dno longer fails on a job that has never run- A scheduler job that runs one of Grav's own command line scripts now works when the scheduler is triggered from the web rather than from cron. Those jobs used to fail with "env: php: No such file or directory", because the web server does not have php on its path
- A scheduler job registered as a whole command line, such as
bin/plugin myplugin sync, now runs. Only the executable and its arguments given separately used to work, so a job written the other way looked for a file whose name contained spaces and failed every time it ran - A site served from a subpath by a proxy no longer loses that subpath when a trailing slash is redirected, which previously sent visitors outside the site. The homepage of such a site also no longer redirects to the bare domain. Thanks to @wakqasahmed for the fix #3822
- With
force_sslturned on, a page that does not exist now redirects to HTTPS like every other page, instead of serving the 404 over plain HTTP. Thanks to @wakqasahmed for the fix #3703 - Image settings are no longer applied to audio, video, SVG or document media. An embedded MP3 kept its player instead of being turned into a linked thumbnail, and media URLs no longer pick up stray
loading,decodingandfetchpriorityvalues, which happened on every site whether or not those settings had been changed. Thanks to @wakqasahmed for the fix #4264
- The
v2.0.21 2 weeks ago
-
- Form fields no longer print their HTML attributes as text above the field, a problem the Twig update in 2.0.20 introduced on every form #4256
- A custom text escaper registered by a plugin now works again, instead of stopping the page with an error the first time a template used it
v2.0.20 2 weeks ago
-
- Updated the bundled Twig fork to the current 3.x, picking up the correctness and sandbox improvements from the 3.27 and 3.28 releases.
- Grav now runs on Twig 3.28 and newer, which tightened the escaping method that Grav's compatibility shim replaces and would otherwise stop the site with a server error.
- The Twig content sandbox now accepts the list of tests a template uses, which newer Twig versions hand to it and will require from Twig 4.
- The bundled Nginx configuration now sets caching headers for images, fonts, stylesheets and scripts, so visitors stop re-downloading them on every page.
- Script and style files whose name already contains a version, such as those the Admin panel ships, are cached permanently in that same configuration, because a change always produces a new name.
-
- [security] Page content can no longer register a script or stylesheet through the Twig content sandbox, and asset URLs are now escaped where the tag is built, closing a way to inject markup into a rendered page.
- [security] The
read_filecapability no longer includes the user data folder by default, so page content can no longer be used to publish form submissions and other stored data. - [security] A proxy address that carries a username and password is now hidden from sandboxed page content, matching the other credentials already redacted there.
- [security] Custom Twig sandbox denial rules now take effect regardless of how the class name is capitalised, and can no longer be silently bypassed through a parent class or interface.
- A damaged page cache file is now rebuilt from the original page instead of stopping the site with a server error #4239
- Images and links in page content now work when the file name contains a colon, such as a screenshot named after a timestamp #3933
- A page that sets a full web address as its canonical route now uses that address on its own, instead of joining it onto the site's own address and breaking sitemaps and canonical links #4023
- Turning on asset timestamps now gives each stylesheet and script its own marker taken from when that file last changed, so editing one file no longer waits on an unrelated change before visitors see it #4049
- A canonical route set through the Flex pages API is now saved as written, instead of being stored in a form it could never be read back from.
- Flex directory blueprints no longer lose the fields the Flex Objects plugin adds when something reads the directory early in a request #160
- The scheduler's generated cron command now names the site's environment when that environment has its own configuration, and each run records which environment it used, so custom jobs defined in
user/env/<host>/no longer fail silently from cron #4248 - Audio and video players generated by
media.html()no longer carry analtattribute, which isn't valid on those elements; any alternative text is kept as an accessible label instead, so the markup passes validation #3540
v2.0.19 3 weeks ago
-
- You can now tighten the Twig content sandbox below its built-in defaults with new
denied_*settings insecurity.yaml. - The "Twig in Content" report can show the effective sandbox policy, so you can see exactly what page content is allowed to do.
- You can now tighten the Twig content sandbox below its built-in defaults with new
-
- A theme or plugin that ships its own
.htaccesscan no longer switch off the protection on its own folder, which used to leave its configuration and template files downloadable #4236 - Twig in page content now renders on new installs by default, instead of appearing as raw text until the setting was turned on.
- The long Twig sandbox allowlists now ship built into Grav, so
security.yamlonly records your own additions and future security updates to the defaults reach every site. - Existing sites that had trimmed those allowlists to tighten them keep exactly that policy after upgrading, now recorded as explicit
denied_*entries. - Removed two rarely-used Twig sandbox switches (
loggingandadmin_hint); both behaviours are now always on.
- A theme or plugin that ships its own
-
- The content cross-site scripting check no longer objects to harmless
<option>and<select>markup, whose original issue is fixed in the form field that actually rendered it. - Sites running with the Twig 2 compatibility setting no longer crash with a server error on every page once an update clears the template cache #4235
- The content cross-site scripting check no longer objects to harmless
v2.0.24
Latest
2 days ago
-
- A dependency can now name the generation of Grav it is for. A plugin that supports both 1.7 and 2.0 often needs a different version of the same dependency on each, so a
dependenciesentry takes an optionalgravkey:- { name: form, version: '>=9.1.0', grav: '2.0' }. Entries without it apply everywhere, so existing blueprints are unchanged. See Plugin Compatibility - A new
system.images.progressive_jpegsetting, on by default, controls whether resized and cached JPEGs are saved as progressive
- A dependency can now name the generation of Grav it is for. A plugin that supports both 1.7 and 2.0 often needs a different version of the same dependency on each, so a
-
- Installing a package whose dependency is not in the GPM index now says so and carries on, instead of stopping the command with a PHP fatal error. A plugin that still asks for the Grav 1.7 admin plugin was enough to trigger it getgrav/grav-premium-issues#618
- A plugin that asks for the
adminplugin now has that read as Admin 2 on Grav 2, so a plugin written for both 1.7 and 2.0 installs instead of failing on a dependency that cannot exist there. The version it asks for is not carried over, because it describes the old admin's numbering getgrav/grav-premium-issues#618 - Any other dependency that cannot be installed on this generation of Grav is now left out of the install rather than attempted and failed
- A JSON request whose body is a bare scalar (
"text",12345,true) sent withContent-Type: application/jsonno longer answers a 500 from the request pipeline before any route runs. It is treated as an empty body, so a plugin's webhook or API route gets to answer it, log it and refuse it itself. Witherrors.displayon, the old failure also printed a stack trace with server paths to whoever sent it - An image default such as
resizeset insystem.images.defaultsworks again. Every image manipulation was being skipped since 2.0.22, leaving only the loading and decoding hints #4282 - Settings such as
loading: lazynow stay on the image whenlinkis also on, instead of moving onto the surrounding link where the browser never sees them #4282 - Resized and cached JPEGs are saved as progressive again, so a photo appears as a whole blurry image that sharpens instead of filling in one line at a time. It has been Grav's default since 2014 but silently stopped working in 1.4.6 #4284
- A media file's own settings from
media.yamlare no longer overwritten the moment the image is opened, so custom default filters for an image type work again - A URL typed with a trailing slash no longer errors out on a site that has the debugger switched on and the Login plugin protecting page media, because the debug bar is now skipped on redirects, where there is no page to put it on #4280
- With the optional
system.session.read_and_closesetting turned on, a change made to the session early in a request is no longer thrown away by a later write in that same request, and a message added just before a redirect now reaches the page it was meant for #4281
v2.0.23 4 days ago
-
composer.jsonnow declares thectypeandsessionextensions it has always used, and suggestsfileinfoandsimplexml. Installing withcomposer create-projecton a machine missing one of these no longer quietly walks back to a years-old release of Grav instead of failing #4273
-
- Reading the browser name, platform or version no longer raises a PHP deprecation notice when a request arrives with no user agent, which is every request from a bare script or a health check
- Opening Clockwork before anything has been profiled, on a fresh install or right after
bin/grav clear, now reports that there is no data yet instead of failing with a 500 - A
GRAV_CONFIG__override set totrueorfalsenow reaches the configuration as a real yes/no value instead of the word itself, so switching something off from a.envfile or the server environment actually switches it off. Thanks to @nerdyjan for the report and @AdilAzhariOmsan for the fix #4277 Uri::ip()now reads the visitor's address from$_SERVER, falling back to the environment, so hosts that don't hand request variables to PHP's environment no longer report every visitor asUNKNOWN. Anything that counts per address there, such as the Login plugin's failed-login lockout and per-IP rate limiting, had been sharing a single bucket. Thanks to @sandymac #2507- On those same hosts the
system.http_x_forwardedoptions forip,client_ipandcf_connecting_iphad no effect at all, and now work as documented. If you turned one on and saw nothing change, turn it back off unless the site really is behind a proxy that overwrites that header
v2.0.22 6 days ago
-
- The
url()Twig function now takes a language, so a link to a route that isn't a page - a search page, a form action - can carry the site's language prefix:{{ url('/search', lang=true) }} - Blueprints can use a
mediafield type, which saves a picked file as its path and keeps a list of them when the field allows more than one - Page collections can now exclude one or more template types with
notOfType(), the counterpart to the existingofType()#3910 - The scheduler can now run the jobs that have missed their scheduled time, rather than only the ones due this very minute. Run it with
bin/grav scheduler --catch-up, which is what you want on a site that has no cron entry set up
- The
-
- Twig in page content can no longer read the site's configuration through the
print_r,vardump,json_encode,yaml_encodeandstringfilters. The check that was meant to stop it had been asking whether the whole site was in sandbox mode, which Grav never does — it decides per template — so it had been letting everything through. Thanks to @Vectrain51 - The
|map,|filterand|reduceTwig filters now check for themselves whether the template calling them is sandboxed, rather than relying on Twig to work it out. Thanks to @DhiyaneshGeek - The scheduler no longer rebuilds a queued job from an unsigned queue file, so a file written into the queue folder by something other than Grav can at most re-run a job the site was already set up to run. The page index also refuses to build objects while reading its cache. Thanks to @elite0529
- Plugin and theme descriptions are now rendered in Parsedown's safe mode before admin displays them, so a description carrying raw HTML shows as text rather than being rendered. Thanks to @alham-rizvi
- The debugger's Clockwork data endpoint now answers only requests coming from the server itself, or requests presenting the secret set in the new
debugger.tokenoption. Cookies and API tokens are no longer recorded in profiler data whatever thecensoredoption is set to - Building a URL is now faster, which adds up over the hundreds of asset and link URLs a single page render produces
- The scheduler now records whether a run was started by cron or by hand, and a run you started yourself no longer counts as evidence that cron is set up
bin/grav scheduler -rnow records the run against each job, the same as a scheduled run, so the next run knows what has already happened- A field that is rejected only for being too long or too short now says so, and gives both the length submitted and the limit, instead of the same "Invalid input" any other bad value gets
- Multiline fields no longer carry a length limit low enough to affect real writing. Set
max: 0on a field to remove the limit altogether
- Twig in page content can no longer read the site's configuration through the
-
- The
|reduceTwig filter now actually reduces. It was running the|mapcode by mistake and throwing away the starting value, so[1,2,3]|reduce((c, v) => c + v, 0)gave back a list instead of6. Thanks to @DhiyaneshGeek - A form field's
minlengthandmaxlengthare now checked when the form is submitted, not only by the browser. They were being written into the page as HTML attributes but ignored on the server, so anything that skipped the browser's own check went straight through #642 - A field with a
stepset now accepts the lengths and counts that land on a step, and rejects the ones that do not. The check was the wrong way round, so it rejected exactly the values it was meant to allow - A long page can be saved from the admin again. Page content was capped at 65,536 characters, so anything longer than roughly twenty pages of text was refused, and the only way to edit it was to write the file directly #3643
- A site installed in a subfolder no longer mangles URLs whose path repeats the install folder's name, such as an image at
/images/subdir/photo.jpgon a site installed at/subdir - A link to a page that carries a query string or an anchor, such as
/blog?page=2, now resolves to the page and keeps its language prefix, instead of being passed through as a plain path - On a site installed in a subfolder, links written with the full path now resolve to the page, so they pick up the site's language and page extension
- A cache folder that the web server cannot write to no longer takes the whole site down. Grav now logs a warning naming the folder and serves the request without the cache, so the front end and the admin both stay reachable and the Problems plugin can report what is wrong #4260
- The same failure writing
user/config/versions.yamlno longer stops the site either #3688 - Errors about a file that cannot be written now name the folder and say whether it is missing or not writable, instead of only reporting the file
- A session cookie name starting with
__Secure-or__Host-now keeps that prefix and is sent with the settings browsers require for it, so the extra protection those prefixes give actually applies. Thanks to @wakqasahmed for the fix #3773 Note: sites whosesystem.session.namecontains capitals, underscores or a leading or trailing dash will get a slightly different cookie name after this update, which signs their users out once. - The scheduler no longer reports that cron is not set up when the crontab entry is written in a valid but slightly different style, such as one using
&&or an absolute path tobin/grav - On a site with a custom scheduler job of its own, looking up a job by name no longer misses every job the system and its plugins register, so the backup and cache jobs can be found and run individually
- A scheduler job that finished its work but could not then write its output file, send its notification email or run its callback no longer aborts the whole run. The remaining jobs run, every result is still recorded, and the problem is written to the log
- A scheduler job registered without a schedule of its own no longer causes an error when its next run time is worked out
bin/grav scheduler -jno longer fails on a site with jobs registered by a plugin, and-dno longer fails on a job that has never run- A scheduler job that runs one of Grav's own command line scripts now works when the scheduler is triggered from the web rather than from cron. Those jobs used to fail with "env: php: No such file or directory", because the web server does not have php on its path
- A scheduler job registered as a whole command line, such as
bin/plugin myplugin sync, now runs. Only the executable and its arguments given separately used to work, so a job written the other way looked for a file whose name contained spaces and failed every time it ran - A site served from a subpath by a proxy no longer loses that subpath when a trailing slash is redirected, which previously sent visitors outside the site. The homepage of such a site also no longer redirects to the bare domain. Thanks to @wakqasahmed for the fix #3822
- With
force_sslturned on, a page that does not exist now redirects to HTTPS like every other page, instead of serving the 404 over plain HTTP. Thanks to @wakqasahmed for the fix #3703 - Image settings are no longer applied to audio, video, SVG or document media. An embedded MP3 kept its player instead of being turned into a linked thumbnail, and media URLs no longer pick up stray
loading,decodingandfetchpriorityvalues, which happened on every site whether or not those settings had been changed. Thanks to @wakqasahmed for the fix #4264
- The
v2.0.21 2 weeks ago
-
- Form fields no longer print their HTML attributes as text above the field, a problem the Twig update in 2.0.20 introduced on every form #4256
- A custom text escaper registered by a plugin now works again, instead of stopping the page with an error the first time a template used it
v2.0.20 2 weeks ago
-
- Updated the bundled Twig fork to the current 3.x, picking up the correctness and sandbox improvements from the 3.27 and 3.28 releases.
- Grav now runs on Twig 3.28 and newer, which tightened the escaping method that Grav's compatibility shim replaces and would otherwise stop the site with a server error.
- The Twig content sandbox now accepts the list of tests a template uses, which newer Twig versions hand to it and will require from Twig 4.
- The bundled Nginx configuration now sets caching headers for images, fonts, stylesheets and scripts, so visitors stop re-downloading them on every page.
- Script and style files whose name already contains a version, such as those the Admin panel ships, are cached permanently in that same configuration, because a change always produces a new name.
-
- [security] Page content can no longer register a script or stylesheet through the Twig content sandbox, and asset URLs are now escaped where the tag is built, closing a way to inject markup into a rendered page.
- [security] The
read_filecapability no longer includes the user data folder by default, so page content can no longer be used to publish form submissions and other stored data. - [security] A proxy address that carries a username and password is now hidden from sandboxed page content, matching the other credentials already redacted there.
- [security] Custom Twig sandbox denial rules now take effect regardless of how the class name is capitalised, and can no longer be silently bypassed through a parent class or interface.
- A damaged page cache file is now rebuilt from the original page instead of stopping the site with a server error #4239
- Images and links in page content now work when the file name contains a colon, such as a screenshot named after a timestamp #3933
- A page that sets a full web address as its canonical route now uses that address on its own, instead of joining it onto the site's own address and breaking sitemaps and canonical links #4023
- Turning on asset timestamps now gives each stylesheet and script its own marker taken from when that file last changed, so editing one file no longer waits on an unrelated change before visitors see it #4049
- A canonical route set through the Flex pages API is now saved as written, instead of being stored in a form it could never be read back from.
- Flex directory blueprints no longer lose the fields the Flex Objects plugin adds when something reads the directory early in a request #160
- The scheduler's generated cron command now names the site's environment when that environment has its own configuration, and each run records which environment it used, so custom jobs defined in
user/env/<host>/no longer fail silently from cron #4248 - Audio and video players generated by
media.html()no longer carry analtattribute, which isn't valid on those elements; any alternative text is kept as an accessible label instead, so the markup passes validation #3540
v2.0.19 3 weeks ago
-
- You can now tighten the Twig content sandbox below its built-in defaults with new
denied_*settings insecurity.yaml. - The "Twig in Content" report can show the effective sandbox policy, so you can see exactly what page content is allowed to do.
- You can now tighten the Twig content sandbox below its built-in defaults with new
-
- A theme or plugin that ships its own
.htaccesscan no longer switch off the protection on its own folder, which used to leave its configuration and template files downloadable #4236 - Twig in page content now renders on new installs by default, instead of appearing as raw text until the setting was turned on.
- The long Twig sandbox allowlists now ship built into Grav, so
security.yamlonly records your own additions and future security updates to the defaults reach every site. - Existing sites that had trimmed those allowlists to tighten them keep exactly that policy after upgrading, now recorded as explicit
denied_*entries. - Removed two rarely-used Twig sandbox switches (
loggingandadmin_hint); both behaviours are now always on.
- A theme or plugin that ships its own
-
- The content cross-site scripting check no longer objects to harmless
<option>and<select>markup, whose original issue is fixed in the form field that actually rendered it. - Sites running with the Twig 2 compatibility setting no longer crash with a server error on every page once an update clears the template cache #4235
- The content cross-site scripting check no longer objects to harmless