Skip to content
Open

5.9 #756

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
818f146
!important
AugustMiller Nov 14, 2025
9628574
Easier color inversion?
AugustMiller Nov 14, 2025
5b9f480
Entries: sources, pages, UI labels
AugustMiller Nov 14, 2025
c609654
Figure component?
AugustMiller Nov 14, 2025
8a5529a
Condition rule case-sensitivity
AugustMiller Dec 3, 2025
f60966a
Asset transforms
AugustMiller Dec 3, 2025
12eee26
Remove empty warning box
AugustMiller Dec 3, 2025
9c29acc
Related pages for checkboxes fields
AugustMiller Dec 3, 2025
b103449
Assets fields settings
AugustMiller Dec 5, 2025
de03e9d
Matrix fields
AugustMiller Dec 5, 2025
152b3c2
(Matrix field settings image)
AugustMiller Dec 5, 2025
31322c1
Heading sequence
AugustMiller Dec 5, 2025
7a8ac65
Entry vs element is an important distinction here
AugustMiller Dec 5, 2025
437e6c6
Lightswitch field settings
AugustMiller Dec 5, 2025
7cd50e9
Explicit validation action
AugustMiller Dec 5, 2025
efcbeb6
Heading level
AugustMiller Dec 8, 2025
8fd8dbc
Customizing authorization
AugustMiller Dec 8, 2025
cab6516
Drafts, revisions
AugustMiller Dec 8, 2025
8208039
New hash filter argument
AugustMiller Dec 8, 2025
8ec1230
+a
AugustMiller Dec 8, 2025
d934c75
Magic site variables and interpolation
AugustMiller Dec 9, 2025
2a89f8e
Accuracy is important :)
AugustMiller Dec 9, 2025
cd687d1
Fix ref links
AugustMiller Dec 9, 2025
aa7ed67
.env
AugustMiller Dec 9, 2025
1f907c8
Link field behavior (actually changed in 5.5.0)
AugustMiller Dec 9, 2025
8d0fd57
New Twig function: randomString()
AugustMiller Dec 9, 2025
04455d9
gql() Twig function edits
AugustMiller Dec 10, 2025
21c921f
uuid() Twig function
AugustMiller Dec 11, 2025
09a5061
url() and siteUrl() Twig function improvements
AugustMiller Dec 11, 2025
a29f500
Minor cleanup on Twig tags
AugustMiller Dec 11, 2025
34832ff
Additional note about entry type UI labels
AugustMiller Dec 11, 2025
b4f7eaf
Money/currency filters
AugustMiller Dec 11, 2025
1e4d713
Notes on sources (re: pages) and copying entries
AugustMiller Dec 11, 2025
367b533
New export types, element actions edits
AugustMiller Dec 12, 2025
40d46f5
Highlights for tagging `Since` info
AugustMiller Dec 12, 2025
66ccd51
Note missing "content block" element type
AugustMiller Dec 12, 2025
321453f
Address labels and other settings
AugustMiller Dec 12, 2025
34bf93b
Routing cleanup
AugustMiller Dec 15, 2025
216488e
Relational fields
AugustMiller Dec 17, 2025
5ad3462
Addresses fields cleanup
AugustMiller Dec 17, 2025
07660c9
Use <mark>, unstyle
AugustMiller Dec 17, 2025
66f5e7a
Note about address field layout
AugustMiller Dec 17, 2025
ed5488b
GFM
AugustMiller Dec 17, 2025
333f7ba
Cleanup, clarifications
AugustMiller Dec 17, 2025
a20b2a4
Don't skip heading levels
AugustMiller Dec 17, 2025
7e8865a
`invoke` Twig filter
AugustMiller Dec 17, 2025
9ca954e
CP nav icons, badges, etc.
AugustMiller Dec 17, 2025
64eddc2
Finish thought about sender
AugustMiller Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/.vuepress/components/Fig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<figure>
<img :src="src">
<figcaption :html="caption"></figcaption>
</figure>
</template>

<script>
export default {
props: {
src: String,
caption: String,
},
};
</script>
59 changes: 54 additions & 5 deletions docs/.vuepress/components/Since.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
<template>
<span :class="{ 'since': true, 'since--with-highlight': $slots.default }">
<mark v-if="$slots.default" class="since__highlight">
<slot></slot>
</mark>

<a
class="since"
class="since__label"
:href="releaseUrl"
:title="parsedDescription"
target="_blank">
{{ ver }}<span class="plus">+</span>
</a>
{{ ver }}<span class="plus">+</span>
</a>
</span>
</template>

<script>
Expand Down Expand Up @@ -58,16 +64,59 @@ export default {

<style lang="postcss" scoped>
.since {
&--with-highlight {}
}

.since__label {
@apply inline-block rounded-md py-1 px-1;
background-color: var(--custom-block-bg-color);
border-color: var(--custom-block-border-color);
border-color: var(--border-color);
font-size: 13px;
vertical-align: super;
line-height: 12px;
text-decoration: none !important;

&:hover {
background-color: var(--code-bg-color);
text-decoration: none;
}

.since--with-highlight & {}
}

.since__highlight {
text-decoration: underline;
text-decoration-style: dotted;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
color: currentColor;
background-color: transparent;

/*
&::before,
&::after {
display: inline-block;
height: 1.3em;
width: 4px;
border-width: 1px 0;
border-radius: 3px;
border-color: currentColor;
opacity: 0.25;
position: relative;
bottom: -0.3em;
vertical-align: baseline;
}

&::before {
content: '';
border-left-width: 1px;
margin-right: 0.15em;
}

&::after {
content: '';
border-right-width: 1px;
margin-left: 0.15em;
}
*/
}
</style>
1 change: 1 addition & 0 deletions docs/.vuepress/theme/styles/base.pcss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
:root {
--text-color: theme("colors.slate");
--text-color-muted: #6D7480;
--text-color-inverted: theme("colors.white");
--ui-component-color: #8491A4;
--bg-color: theme("colors.white");
--sidebar-bg-color: theme("colors.softer");
Expand Down
1 change: 1 addition & 0 deletions docs/.vuepress/theme/styles/color-mode.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
html:not([class]) {
--text-color: theme("colors.white");
--text-color-muted: #818EA2;
--text-color-inverted: theme("colors.slate");
--ui-component-color: theme("colors.gray.600");
--bg-color: theme("colors.gray.900");
--sidebar-bg-color: rgba(0, 0, 0, 0.1);
Expand Down
21 changes: 19 additions & 2 deletions docs/5.x/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ CRAFT_ENVIRONMENT=dev
# ...and comments!
```

These values can be referenced in your config files by calling [App::env()](craft5:craft\helpers\App::env()), or using them directly in a [control panel setting](#control-panel-settings). Use of PHP’s `getenv()` directly is discouraged, due to [issues with thread-safety](https://github.com/craftcms/cms/issues/3631). The equivalent [`getenv()` Twig function](reference/twig/functions.md#getenv) uses `App::env()`, and is therefore fine to use in templates.
These values can be referenced in your config files by calling [App::env()](craft5:craft\helpers\App::env()), or using them directly in a [control panel setting](#control-panel-settings). Use of PHP’s `getenv()` directly is discouraged, due to [issues with thread-safety](https://github.com/craftcms/cms/issues/3631). The [`getenv()` Twig function](reference/twig/functions.md#getenv) is a wrapper around our custom `App::env()` method, and therefore fine to use in templates.

Craft doesn’t require your variables to follow any kind of naming convention, but it will automatically discover [some specific environment variables](#environment-overrides) for general and database settings.

Expand All @@ -65,6 +65,13 @@ For most installations, the `.env` file is the only place where secrets should b
Some platforms (especially those with ephemeral filesystems, like [Craft Cloud](https://craftcms.com/cloud)) provide a GUI for managing environment variables in lieu of using a `.env` file, and will automatically inject them when the server or process starts. `App::env()` is still the recommended method for retrieving environment variables set in this way.
:::

Once Craft has fully initialized and the current [site](system/sites.md) is known, it adds two additional environment variables: <Since ver="5.9.0" feature="$CRAFT_SITE and $CRAFT_SITE_UPPER variables" />

| Variable Name | Value |
| --- | --- |
| `CRAFT_SITE` | The current site’s handle. |
| `CRAFT_SITE_UPPER` | The current site’s handle, converted to `SCREAMING_SNAKE_CASE`. |

#### Nested Variables

In your `.env` file, one variable can reference another:
Expand All @@ -75,7 +82,7 @@ PRIMARY_SITE_URL="https://${BASE_HOSTNAME}"
GLOBAL_SITE_URL="https://global.${BASE_HOSTNAME}"
```

Depending on your infrastructure, this may also be possible at other points in process of loading environment variables. The example above works thanks to `vlucas/phpdotenv`; Docker (and therefore DDEV) share this general syntax, but not all variables are available at each step as the container boots up—so interpolation is best left until this last stage. Earlier layers may allow interpolation to be escaped so that it is only evaluated by a later one:
Depending on your infrastructure, this may also be possible at other points in process of loading environment variables. The example above works [thanks to `vlucas/phpdotenv`](https://github.com/vlucas/phpdotenv?tab=readme-ov-file#nesting-variables), the default `.env` file loader; Docker (and therefore DDEV) share this general syntax, but not all variables are available at each step as the container boots up—so interpolation is best left until this last stage. Earlier layers may allow interpolation to be escaped so that it is only evaluated by a later one:

```bash
# Here, we escape the beginning interpolation token with a backslash (\):
Expand All @@ -88,6 +95,8 @@ Craft does not know or indicate when substitutions have occurred—it only sees
Variables with substitutions will only be written back into the environment when using one of the [mutable loaders](https://github.com/vlucas/phpdotenv?tab=readme-ov-file#immutability-and-repository-customization).
:::

[Control panel settings](#control-panel-settings) can also use interpolation.

### Entry Script

Craft will also respond to a handful of [specific environment variables or PHP constants](#bootstrap-config), as long as they are set prior to bootstrapping the application in your entry script. The [starter project](repo:craftcms/craft) shares a `bootstrap.php` file between `web/index.php` and the `craft` executable to consolidate the definition of constants.
Expand Down Expand Up @@ -423,6 +432,14 @@ You may combine aliases and environment variables with additional path segments,
Plugins can add support for environment variables and aliases in their settings as well. See [Environmental Settings](extend/environmental-settings.md) to learn how.
:::

#### Interpolation <Since ver="5.9.0" feature="Nested interpolation of environment variables" />

Environment variables can be used _anywhere_ in a string that is ultimately passed to <craft5:craft\helpers\App::env()> or <craft5:craft\helpers\App::parseEnv()>.
For example, you could parameterize the subdomain portion of a group of [sites](system/sites.md) **Base URL** as `https://$SUBDOMAIN_CORPORATE.$DOMAIN_BASE/en` or `https://$SUBDOMAIN_B2B.$DOMAIN_BASE/en`.

An additional syntax (similar to platform-dependent [nested variables](#nested-variables)) brings dynamic resolution to settings stored in project config—but instead of concatenating values, Craft evaluates references inside `${...}` as part of building the variable name itself.
Combined with the special [`CRAFT_SITE_UPPER` variables](#env), you can make settings like **System Email Address** more reactive: `$MAILER_FROM_${CRAFT_SITE_UPPER}` would first flatten `${CRAFT_SITE_UPPER}` to something like `ACME_CORPORATE`, then look for the environment variable `MAILER_FROM_ACME_CORPORATE`. A request to a different site might end up trying to resolve the variable `MAILER_FROM_ACME_LABS` or `MAILER_FROM_ACME_B2B`.

### Templates and Modules

#### Accessing Config Values
Expand Down
2 changes: 2 additions & 0 deletions docs/5.x/development/element-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ To make a query case-_insensitive_, pass an object with the special `caseInsensi

[Dropdown](../reference/field-types/dropdown.md), [Radio](../reference/field-types/radio.md), [Checkboxes](../reference/field-types/checkboxes.md), and other fields whose only allowable values are determined by the field’s configuration are _always_ handled in a case-sensitive way.

Textual condition rules in [custom sources](system/elements.md#sources) and [field layouts](system/fields.md#conditions) are always handled case-insensitively. <Since ver="5.9.0" description="Condition rules were made case-insensitive in {product} {ver}" />

### Reusing Queries

Sometimes, you might want to run a number of similar queries. Take this case, where we want to show some information about upcoming events at a library:
Expand Down
8 changes: 4 additions & 4 deletions docs/5.x/development/image-transforms.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Image Transforms

Instead of requiring content editors to upload images at a specific sizes, Craft lets you define “image transforms” for automatically manipulating images in predefined ways, or on-demand. Transforms are _non-destructive_, meaning they have no effect on the original uploaded image.
Instead of requiring content editors to upload images at a specific sizes, Craft lets you define “image transforms” to automatically manipulate images in predefined ways, or on-demand. Transforms are _non-destructive_, meaning they have no effect on the original uploaded image.

<!-- more -->

Transforms can be defined in the control panel or directly from your templates and GraphQL queries.
Transforms can be defined in the control panel as [named transforms](#named-transforms) or [directly from your templates](#defining-transforms-in-your-templates) and GraphQL queries.

## Named Transforms

Named transforms are created from the [control panel](../system/control-panel.md) by navigating to **Settings** → **Assets** → **Image Transforms** and press **New Transform**.
Named transforms are created from the [control panel](../system/control-panel.md) by navigating to <Journey path="Settings, Assets, Image Transforms" /> and press **+ New image transform**.

<BrowserShot
url="https://my-craft-project.ddev.site/admin/settings/assets/transforms/new"
:link="false"
caption="Creating a new predefined asset transform in the control panel."
id="settings"
:maxHeight="600">
<img src="../images/assets-transforms.png" alt="Asset transform edit screen">
<img src="../images/asset-transform-config.png" alt="Asset transform edit screen">
</BrowserShot>

<Todo notes="Add POI/waypoints to this screen" />
Expand Down
80 changes: 56 additions & 24 deletions docs/5.x/extend/cp-section.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Control Panel Sections

Modules and plugins can add new sections to the control panel using the [EVENT_REGISTER_CP_NAV_ITEMS](craft5:craft\web\twig\variables\Cp::EVENT_REGISTER_CP_NAV_ITEMS) event:
Modules and plugins can add new sections to the control panel using the [EVENT_REGISTER_CP_NAV_ITEMS](craft5:craft\web\twig\variables\Cp::EVENT_REGISTER_CP_NAV_ITEMS) event.
Plugins also have a special [`getCpNavItem()` method](#control-panel-sections).

```php
use craft\events\RegisterCpNavItemsEvent;
Expand All @@ -16,8 +17,8 @@ public function init()
Cp::EVENT_REGISTER_CP_NAV_ITEMS,
function(RegisterCpNavItemsEvent $event) {
$event->navItems[] = [
'url' => 'section-url',
'label' => 'Section Label',
'label' => 'Plugin Zone',
'url' => 'myplugin-zone',
'icon' => '@mynamespace/path/to/icon.svg',
];
}
Expand All @@ -31,33 +32,55 @@ Each item within the [navItems](craft5:craft\events\RegisterCpNavItemsEvent::$na

- `url` – The URL that the nav item should link to. (It will be run through <craft5:craft\helpers\UrlHelper::cpUrl()>.)
- `label` – The user-facing nav item label.
- `icon` – The path to the icon SVG that should be used. (It can begin with an alias.)
- `badgeCount` _(optional)_ – The badge count that should be displayed in the nav item.
- `subnav` _(optional)_ – An array of subnav items that should be visible when your section is accessed. (See [Subnavs](#subnavs).)
- `icon` _(optional)_ – The path to the icon SVG that should be used. (It can begin with an alias.)
- `fontIcon` _(optional)_ – A character/ligature from Craft’s font icon set.
- `badgeCount` _(optional)_ – The [badge count](#badges) that should be displayed in the nav item.
- `subnav` _(optional)_ – An array of additional, nested items that should be visible when your section is accessed, or when a user expands the primary item. (See [Subnavs](#subnavs), below.)


For Craft to properly designate an item as “active,” its `url` must be registered with a relative path to the plugin or module’s control panel section. Any `subnav` paths should begin with `url` in order to appear selected when active.
For Craft to properly designate an item as “active,” its `url` must be registered with a _path_ to the plugin or module’s control panel section.
This holds true for `subnav` URLs, as well—their `url` must begin with the parent item’s `url` to appear selected.

## Subnavs

If your section has a sub-navigation, each subnav item within your `subnav` array should be represented by a sub-array with `url` and `label` keys:
If your section has a sub-navigation, each subnav item within your `subnav` array should be represented by a sub-array with mandatory `url` and `label` keys, and optional `badgeCount`, <Since ver="5.9.0" feature="Icons for nested navigation items in the control panel">`icon`, and `fontIcon`</Since> keys:

```php
'subnav' => [
'foo' => ['label' => 'Foo', 'url' => 'section-url/foo'],
'bar' => ['label' => 'Bar', 'url' => 'section-url/bar'],
$event->navItems[] = [
// ...

// Display a subnav badge count by adding the optional `badgeCount` key:
'baz' => ['label' => 'Baz', 'url' => 'section-url/baz', 'badgeCount' => 5],
],
'subnav' => [
'attractions' => [
'label' => 'Attractions',
'url' => 'plugin-zone/attractions',
'icon' => 'pegasus',
],
'reservations' => [
'label' => 'Reservations',
'url' => 'plugin-zone/reservations',
'icon' => 'calendar-star',
],
// Display a subnav badge count by adding the optional `badgeCount` key:
'policies' => [
'label' => 'Policies',
'url' => 'plugin-zone/policies'
'badgeCount' => 3,
],
],
];
```

Your templates can specify which subnav item should be selected by setting a `selectedSubnavItem` variable to the key of the nav item:
Your templates can specify which subnav item should be selected by setting `selectedSubnavItem` to the key of a nav item:

```twig
{% set selectedSubnavItem = 'bar' %}
```

## Badges

Navigation items’ `badgeCount`s are intended to be calculated dynamically.
If this involves costly queries or other blocking operations (like a request to a remote API), consider caching the value.
More guidance on how to use badges can be found in the [Utilities](utilities.md) section.

## Plugin Sections

Plugins that only need to add one section can set the `$hasCpSection` property on their primary plugin class, rather than using the [EVENT_REGISTER_CP_NAV_ITEMS](craft5:craft\web\twig\variables\Cp::EVENT_REGISTER_CP_NAV_ITEMS) event:
Expand All @@ -79,31 +102,34 @@ class Plugin extends \craft\base\Plugin
You can alternatively set a `hasCpSection` value in `composer.json` as noted in the [plugin guide](plugin-guide.md#composer-json). We don’t recommend setting it in both places, however, since the value set in `composer.json` will override your public class property’s value and likely create confusion.
:::

You can modify aspects of the plugin’s control panel nav item by overriding its [getCpNavItem()](craft5:craft\base\PluginInterface::getCpNavItem()) method:
Craft automatically adds a new [user permission](user-permissions.md) for your plugin, and only shows a nav item to users who can access it.

Modify aspects of your plugin’s control panel nav item by overriding its [getCpNavItem()](craft5:craft\base\PluginInterface::getCpNavItem()) method:

```php
public function getCpNavItem(): ?array
{
// Get the base definition (including `label`, `url`, and `icon`)...
$item = parent::getCpNavItem();

// ...customize...
$item['badgeCount'] = 5;
$item['subnav'] = [
'foo' => ['label' => 'Foo', 'url' => 'plugin-handle/foo'],
'bar' => ['label' => 'Bar', 'url' => 'plugin-handle/bar'],
'baz' => ['label' => 'Baz', 'url' => 'plugin-handle/baz'],
// ...
];

// ...and return:
return $item;
}
```

If you do this, Craft will automatically add a new [user permission](user-permissions.md) for your plugin, and only show the nav item for users that have it.

Clicking on a plugin’s section will take the user to `/admin/plugin-handle`, which will attempt to load an `index.html` or `index.twig` template within the plugin’s [template root](template-roots.md) (its `templates/` folder within its base source folder).
Clicking on a plugin’s nav item will take the user to `/{cpTrigger}/plugin-handle`, which will attempt to load an `index.html` or `index.twig` template within the plugin’s [template root](template-roots.md) (its `templates/` folder within its base source folder).

::: tip
See [Control Panel Templates](cp-templates.md) for more information about developing control panel templates.
:::

Alternatively, you can route `/admin/plugin-handle` requests to a controller action (or a different template) by registering a control panel route from your plugin’s `init()` method:
Alternatively, you can route those requests to a controller action (or a different template) by registering a control panel route from your plugin’s `init()` method:

```php
use craft\events\RegisterUrlRulesEvent;
Expand All @@ -123,3 +149,9 @@ public function init()
```

These rules use the same format as those [defined in `routes.php`](../system/routing.md#advanced-routing-with-url-rules). Find additional examples of control panel and site routes in the routing section of the [controllers](controllers.md#routing) documentation.

::: tip
Craft automatically prepends your pattern with the <config5:cpTrigger>.
Including it (or hard-coding it to the default `admin`) may make your routes unreachable.
:::

Binary file added docs/5.x/images/asset-transform-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/5.x/images/assets-transforms.png
Binary file not shown.
Binary file modified docs/5.x/images/drafts-revisions-menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/5.x/images/entries-customize-sources.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/5.x/images/entry-type-edit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/5.x/images/fields-assets-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/5.x/images/fields-matrix-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/5.x/images/route-with-wildcard-token.png
Binary file not shown.
Binary file modified docs/5.x/images/routing-creating-new-route.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion docs/5.x/reference/element-types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ Use the sidebar to find information about a specific element type, or learn abou

<See path="users.md" />

Craft has one additional element type used exclusively by the [content block](../field-types/content-block.md) field.

<hr>

::: tip
Looking for [Matrix](../field-types/matrix.md) blocks? They were converted to [entries](entries.md#nested-entries) during the [upgrade](../../upgrade.md) to Craft 5!
Looking for [Matrix](../field-types/matrix.md) blocks?
They were converted to [entries](entries.md#nested-entries) during the [upgrade](../../upgrade.md) to Craft 5!
:::
Loading