diff --git a/docs/5.x/development/graphql.md b/docs/5.x/development/graphql.md index f1364507c..fd8ef1d58 100644 --- a/docs/5.x/development/graphql.md +++ b/docs/5.x/development/graphql.md @@ -56,59 +56,50 @@ Schema customization is only possible with an admin account and setting (this completely hides the option in the schema customization screen). -Schemas can also grant read-only access to some of your project’s configuration, using the **Query for sites**, **Query for sections**, **Query for entry types**, **Query for volumes**, **Query for user groups**, **Query for category groups**, and **Query for tag groups** components. Each one adds a pair of [settings queries](#settings-queries) (like `sections` and `section`), and a field that links elements to that configuration (like `section` on entries). +Schemas can also grant read-only access to some of your project’s configuration. + +- **Query for sites** +- **Query for sections** +- **Query for entry types** +- **Query for volumes** +- **Query for user groups** +- **Query for category groups** +- **Query for tag groups** + +Each of these permissions adds a pair of [settings queries](#settings-queries) (like `sections` and `section`), and makes the configuration type’s fields accessible via the relevant elements (like `section` on entries, or `volume` on assets). +This supplements existing fields containing primitive references, like `sectionId`, `typeHandle`, or `volumeId`. #### Generate Tokens -A _token_ is required to use any non-public schema. Tokens use the standard “bearer” `Authorization` HTTP header: +A _token_ is required to use any non-public schema. +Tokens are sent using the standard “bearer” `Authorization` HTTP header: ```http Authorization: Bearer {token} ``` -Invalid tokens will produce a 400-level HTTP exception; requests _without_ a token default to the public schema, if it is enabled. More [examples](#examples) of token usage appear below! +Invalid tokens will produce a 400-level HTTP exception; requests _without_ a token default to the public schema, if it is enabled. -To create a token, visit and click **New token**. Every token’s **Name** must be unique, but multiple tokens may be created for each schema. Craft automatically generates a random 32-character access token, which can be copied from the **Authorization Header** section. Regenerate and save a token to revoke access to existing clients. +To create a token, visit and click **New token**. +Every token’s **Name** must be unique, but multiple tokens may be created for each schema. +Craft automatically generates a random 32-character access token, which can be copied from the **Authorization Header** section. +Regenerate and save a token to revoke access to existing clients. -Because tokens are similar to [user groups](../system/user-management.md#user-groups) in the way they grant access to system resources, only admins are allowed to create and modify them. Tokens can also be created via the CLI, with the [`graphql/create-token` command](../reference/cli.md#graphql-create-token). To find a schema’s UUID (required by this command), run `graphql/list-schemas`. +Tokens are similar to [user groups](../system/user-management.md#user-groups) in the way they grant access to system resources, so only admins are allowed to create and modify them. +Tokens can also be created via the CLI, with the [`graphql/create-token` command](../reference/cli.md#graphql-create-token). +To find a schema’s UUID (required by this command), run `graphql/list-schemas`. While [schemas](#define-your-schemas) are tracked in [project config](../system/project-config.md), tokens are only stored in the database and must be created in each environment. ::: danger -Carefully protect tokens for schemas that allow [mutations](#mutations)! These are effectively as dangerous as a user with the equivalent permissions. -::: - -#### Authenticating as a User - -Instead of a schema token, requests can be authenticated with an [OAuth](../system/oauth.md) access token that was issued to a specific user. Apps can get one by having the user sign in and authorize them, or the user can create a personal access token in the control panel. - -Access tokens are sent in the same `Authorization` header as schema tokens: - -```bash -curl \ - -H "Authorization: Bearer {access-token}" \ - -H "Content-Type: application/graphql" \ - -d '{currentUser{id,fullName}}' \ - https://my-project.ddev.site/api -``` - -Craft doesn’t use one of your schemas for these requests. It builds one for the user that authorized the token instead, from the schema components that support authenticated requests and match that user’s [permissions](../system/user-management.md#permissions). For example, entries in a section are only queryable if the user can view entries in it, and drafts, revisions, and disabled elements are only available to users with control panel access. The request runs as that user, so queries only return elements they can view, and mutations are only allowed if they can save, delete, or create drafts of the element in question. Category, tag, and global set components aren’t available to authenticated requests. - -The user also decides how much access a token gets when they authorize an app or create a token, using two OAuth scopes: - -- `graphql:read` (“Read only”) — The token can run queries. -- `graphql:write` (“Read and write”) — The token can run queries and [mutations](#mutations). - -To create a personal access token, go to and click **New token**. This requires an [elevated session](../system/user-management.md#elevated-sessions). Give the token a name, select the scopes it should have, and choose when it expires (7, 30, or 90 days, or one year). The token is only shown once, so copy it right away. The **Tokens** screen also lists the apps you’ve authorized. From there you can rename personal tokens, and revoke tokens or an app’s access. The screen only appears when an API offers personal access tokens. GraphQL does this whenever is on. - -Authenticated requests can use the [`currentUser` query](#the-currentuser-query) to get the user that authorized the token. - -::: tip -Results for authenticated requests are [cached](#caching) separately for each user, and vary by their current permissions. +Carefully protect tokens for schemas that allow [mutations](#mutations)! +These are effectively as dangerous as a user with the equivalent permissions. ::: ## Sending API Requests +A variety of clients can communicate with Craft’s GraphQL API. + ### CURL Assuming your project lives at `https://my-project.ddev.site` and your [route](#create-a-graphql-route) was configured like the example above, you can confirm the [public schema](#define-your-schemas) is working by sending a `{ping}` query to it via the command line: @@ -148,12 +139,12 @@ Additional GraphQL IDEs are available as well: - [GraphQL Playground online](https://www.graphqlbin.com/v2/new) ::: tip -When you’re initially exploring the API, make sure is enabled so the IDE can be informed about the entire schema available to it. +When you are initially exploring the API, make sure is enabled so the IDE can be informed about the entire schema available to it. ::: ### Sending Requests Manually -The GraphQL API can be queried in three ways: +A GraphQL request can have three main formats: 1. **A `GET` request** with the GraphQL query defined by a `query` parameter: ```bash @@ -178,9 +169,12 @@ The GraphQL API can be queried in three ways: https://my-project.ddev.site/api ``` +Note that `curl` implicitly uses `-X POST` when supplying body params with `-d`. +Other clients (like a browser’s `fetch()` API) may require additional setup. + #### Specifying Variables -If you need to specify any [variables](https://graphql.org/learn/queries/#variables) along with your query, then you must send request as a `POST` request with an `application/json` content type, and include a `variables` key in the JSON body alongside your `query`. +If you need to specify any [variables](https://graphql.org/learn/queries/#variables) along with your query, you must send a `POST` request with an `application/json` content type, and include a `variables` key in the JSON body alongside your `query`. ```bash curl \ @@ -194,18 +188,20 @@ curl \ #### Querying a Private Schema -The **Public Schema** is used by default. To query against a different [schema](#define-your-schemas), pass a valid [token](#generate-tokens) under the `Authorization` header. +The **Public Schema** is used by default. +To query against a different [schema](#define-your-schemas), pass a valid [token](#generate-tokens) under the `Authorization` header. ```bash curl \ -H "Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/graphql" \ - -d '{entries{id}}' \ + -d '{ entries { id } }' \ https://my-project.ddev.site/api ``` ::: warning -If you’re unable to query a private schema because of a “missing authorization header,” make sure Craft received it from the web server with a quick post to a test template: +If you’re unable to query private schemas (and every requests ends up using the public schema, or you get an explicit “missing authorization header” error) the `Authorization` header may be getting stripped from the request. +You can test whether the header reaches Craft by to your app by creating a test template and making a `POST` request to it: ```twig {{ craft.app.getRequest().getHeaders().has('authorization') @@ -221,6 +217,42 @@ RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] ``` ::: +#### Authenticating as a User + +GraphQL requests can also be authenticated against a specific user, identified by an access token. +Tokens created via [OAuth](../system/oauth.md) are equivalent to [personal access tokens](../system/oauth.md#personal-access-tokens) created via the control panel. + +These personal access tokens are sent in the same way as [schema tokens](#querying-a-private-schema): + +```bash +curl \ + -H "Authorization: Bearer {access-token}" \ + -H "Content-Type: application/graphql" \ + -d '{ currentUser { id, fullName } }' \ + https://my-project.ddev.site/api +``` + +Craft constructs the schema for an authorized request based on that user’s [permissions](../system/user-management.md#permissions) (and the token’s scopes), rather than a [predefined](#define-your-schemas) one. +For example: entries in a section are only queryable if the user has the corresponding `viewEntries:{uuid}` permission; drafts, revisions, and disabled elements are only available to users with control panel access. +The request runs as that user, so queries only return elements they can view, and mutations are only allowed if they can save, delete, or create drafts of the element in question. + +::: warning +Category, tag, and global sets are not accessible via authenticated requests. +::: + +The user also decides how much access a token gets when they authorize an app or create a token, using two OAuth scopes: + +- `graphql:read` (“Read only”) — The token can run queries. +- `graphql:write` (“Read and write”) — The token can run queries and [mutations](#mutations). + +To create a personal access token, go to and click **New token**. This requires an [elevated session](../system/user-management.md#elevated-sessions). Give the token a name, select the scopes it should have, and choose when it expires (7, 30, or 90 days, or one year). The token is only shown once, so copy it right away. The **Tokens** screen also lists the apps you’ve authorized. From there you can rename personal tokens, and revoke tokens or an app’s access. The screen only appears when an API offers personal access tokens. GraphQL does this whenever is on. + +Authenticated requests can use the [`currentUser` query](#the-currentuser-query) to get the user that authorized the token. + +::: tip +Results for authenticated requests are [cached](#caching) separately for each user, and vary by their current permissions. +::: + ## Examples ### Basic Query @@ -1799,7 +1831,10 @@ This query is used to query for a single category. ### The `currentUser` query -This query returns the user that authenticated the request. It only exists for requests [authenticated as a user](#authenticating-as-a-user), and is always available to them, even if they can’t query other users. Requests that use a schema token don’t have it. It takes no arguments and returns a `UserInterface`, with the same fields as the [`user` query](#the-user-query). +This query returns the user that authenticated the request. +It only exists for requests [authenticated as a user](#authenticating-as-a-user), and is always available to them, even if they can’t query other users. +Requests that use a [schema token](#generate-tokens) don’t have it. +It takes no arguments and returns a `UserInterface`, with the same fields as the [`user` query](#the-user-query). ```graphql query Me { @@ -1811,27 +1846,29 @@ query Me { } ``` +There is no equivalent [mutation](#mutations) for the current user. + ### Settings Queries Each of the read-only [schema components](#define-your-schemas) for your project’s configuration adds a plural query that returns a list, and a singular query that returns the first match (or `null`): -| Schema component | Queries | Type -| - | - | - -| Query for sites | `sites`, `site` | `Site` -| Query for sections | `sections`, `section` | `Section` -| Query for entry types | `entryTypes`, `entryType` | `EntryType` -| Query for volumes | `volumes`, `volume` | `Volume` -| Query for user groups | `userGroups`, `userGroup` | `UserGroup` -| Query for category groups | `categoryGroups`, `categoryGroup` | `CategoryGroup` -| Query for tag groups | `tagGroups`, `tagGroup` | `TagGroup` +| Permission | Queries | GraphQL Type | +| - | - | - | +| Query for sites | `sites`, `site` | `Site` | +| Query for sections | `sections`, `section` | `Section` | +| Query for entry types | `entryTypes`, `entryType` | `EntryType` | +| Query for volumes | `volumes`, `volume` | `Volume` | +| Query for user groups | `userGroups`, `userGroup` | `UserGroup` | +| Query for category groups | `categoryGroups`, `categoryGroup` | `CategoryGroup` | +| Query for tag groups | `tagGroups`, `tagGroup` | `TagGroup` | -All of them accept the same arguments: +All of these queries accept at least three arguments: -| Argument | Type | Description -| - | - | - -| `id`| `[Int]` | Narrows the query results based on their IDs. -| `uid`| `[String]` | Narrows the query results based on their UUIDs. -| `handle`| `[String]` | Narrows the query results based on their handles. +| Argument | Type | Description | +| - | - | - | +| `id`| `[Int]` | Narrows the query results based on their IDs. | +| `uid`| `[String]` | Narrows the query results based on their UUIDs. | +| `handle`| `[String]` | Narrows the query results based on their handles. | ```graphql query Structure { @@ -1886,7 +1923,10 @@ query Articles { ``` ## List of available directives -Directives are not regulated by permissions and they affect how the returned data is displayed. + +Directives affect how field data is returned. +A query can use directives on any field with a relevant type that is accessible in the schema. +However, you must explicitly enable the special `transform` and `parseRefs` directives, as they carry some inherent risks. diff --git a/docs/5.x/images/control-panel-dashboard.png b/docs/5.x/images/control-panel-dashboard.png index 54ecc8fd9..782d27c3c 100644 Binary files a/docs/5.x/images/control-panel-dashboard.png and b/docs/5.x/images/control-panel-dashboard.png differ diff --git a/docs/5.x/images/control-panel-settings.png b/docs/5.x/images/control-panel-settings.png index 63baf85e4..ce72d9314 100644 Binary files a/docs/5.x/images/control-panel-settings.png and b/docs/5.x/images/control-panel-settings.png differ diff --git a/docs/5.x/reference/field-types/color.md b/docs/5.x/reference/field-types/color.md index e707d1660..a05f7da54 100644 --- a/docs/5.x/reference/field-types/color.md +++ b/docs/5.x/reference/field-types/color.md @@ -1,11 +1,15 @@ # Color Fields -Color fields provide a flexible way to store hexadecimal color values. You can define a [palette](#palette) to guide authors , or use an [open-ended input](#custom-colors). +Color fields provide a flexible way to store hexadecimal color values. +You can define a [palette](#palette) to guide authors , or use an [open-ended input](#custom-colors). ![Screenshot of a color field interface in the Craft control panel](../../images/fields-color-ui.png) +Color field values (custom, or from a palette) can be translucent. +Enter an 8-digit hex value (like `#0000ff80`) where the last two digits are the alpha channel, or set the opacity in the color picker, in browsers that support it. + ## Settings Enter an 8-digit hex value (like `#0000ff80`) where the last two digits are the alpha channel, or set the opacity in the color picker, in browsers that support it. - ### Custom Colors Turn on **Allow custom colors** to display a compact input and color preview UI. When used in conjunction with a palette, an additional **Custom…** option is listed at the bottom of the dropdown menu. @@ -34,12 +36,9 @@ Turn on **Allow opacity for custom colors** -When GraphQL is enabled, the **GraphQL Mode** setting determines how the field’s values are represented in the [GraphQL API](#graphql): +When GraphQL is enabled, the **GraphQL Mode** setting determines how the field’s values are exposed in the [GraphQL API](#graphql). -- **Full data** — Values are objects with sub-selections for each color component and format. -- **Hex only** — Values are just the color’s hex string. - -New color fields default to **Full data**. Fields created before Craft 5.12 retain the **Hex only** mode, so existing queries continue to work. +New color fields default to **Full data**; fields created before Craft 5.12 retain the **Hex only** mode, so existing queries continue to work. ## Development @@ -79,11 +78,11 @@ if ($entry->myColorField) { {{ entry.myColorField }} {# output: #e5422b #} {{ entry.myColorField.getHex() }} {# output: #e5422b #} {{ entry.myColorField.getRgb() }} {# output: rgb(229,66,43) #} - {{ entry.myColorField.getHsl() }} {# output: hsl(7,81%,90%) #} + {{ entry.myColorField.getHsl() }} {# output: hsl(7,78%,53%) #} {{ entry.myColorField.getLuma() }} {# output: 0.38820862745098 #} {{ entry.myColorField.getHue() }} {# output: 7 #} - {{ entry.myColorField.getLightness() }} {# output: 90 #} - {{ entry.myColorField.getSaturation() }} {# output: 81 #} + {{ entry.myColorField.getLightness() }} {# output: 53 #} + {{ entry.myColorField.getSaturation() }} {# output: 78 #} {{ entry.myColorField.getRed() }} {# output: 229 #} {{ entry.myColorField.getGreen() }} {# output: 66 #} {{ entry.myColorField.getBlue() }} {# output: 43 #} @@ -94,11 +93,11 @@ if ($entry->myColorField) { echo $entry->myColorField; // output: #e5422b echo $entry->myColorField->getHex(); // output: #e5422b echo $entry->myColorField->getRgb(); // output: rgb(229,66,43) - echo $entry->myColorField->getHsl(); // output: hsl(7,81%,90%) + echo $entry->myColorField->getHsl(); // output: hsl(7,78%,53%) echo $entry->myColorField->getLuma(); // output: 0.38820862745098 echo $entry->myColorField->getHue(); // output: 7 - echo $entry->myColorField->getLightness(); // output: 90 - echo $entry->myColorField->getSaturation(); // output: 81 + echo $entry->myColorField->getLightness(); // output: 53 + echo $entry->myColorField->getSaturation(); // output: 78 echo $entry->myColorField->getRed(); // output: 229 echo $entry->myColorField->getGreen(); // output: 66 echo $entry->myColorField->getBlue(); // output: 43 @@ -114,11 +113,16 @@ You may also access any of the color components as properties: -When a color isn’t fully opaque, its hex value (whether output directly or via `hex`/`getHex()`) includes the alpha channel as two extra digits, like `#e5422b80`. Fully opaque colors are still output as 6-digit hex values. Pass `false` to `getHex()` to always get the 6-digit value, without the alpha channel. +When a color isn’t fully opaque, Craft stores and returns the alpha channel as two extra hexadecimal digits (like `#e5422b80`). +Pass `false` to `getHex()` to discard the alpha channel (if present) and return a standard six-character hexadecimal code. -`ColorData` objects also provide these formats and helpers: +Other `ColorData` methods are named explicitly, rather than parameterized (like `getHsl()` and `getHsla()`): ::: code ```twig @@ -151,7 +155,7 @@ echo $color->getContrastRatio('#ffffff'); // output: 4.0810201549497 ``` ::: -- `alpha` is a number between `0` (fully transparent) and `1` (fully opaque). +- `alpha` is a number between `0` (fully transparent) and `1` (fully opaque), rounded to two or three places. - `rgbChannels` contains just the space-separated red, green, and blue channels, which is handy for defining a CSS custom property that you combine with your own opacity: `rgb(var(--brand) / 50%)`. - `oklch` only includes the alpha channel (`/ 0.5`) when the color isn’t fully opaque. - `contrastColor` is either black (`#000000`) or white (`#ffffff`), whichever contrasts more with the color—useful for choosing a legible text color to display over it. @@ -172,7 +176,9 @@ echo $color->getContrastRatio('#ffffff'); // output: 4.0810201549497 ### GraphQL -When the field’s [GraphQL Mode](#graphql-mode) is **Hex only**, it returns a hex string (which may include an alpha channel). In **Full data** mode, you can select any of these fields: `hex`, `label`, `red`, `green`, `blue`, `alpha`, `hue`, `saturation`, `lightness`, `luma`, `rgb`, `rgba`, `rgbChannels`, `hsl`, `hsla`, `oklch`, `contrastColor`, and `contrastRatio`. +When the field’s [GraphQL Mode](#graphql-mode) is **Hex only**, it returns a hexadecimal string of six or eight characters (when the value includes a relevant [alpha](#translucency-and-other-formats) component). + +In **Full data** mode, you can select any of these fields: `hex`, `label`, `red`, `green`, `blue`, `alpha`, `hue`, `saturation`, `lightness`, `luma`, `rgb`, `rgba`, `rgbChannels`, `hsl`, `hsla`, `oklch`, `contrastColor`, and `contrastRatio`. ```gql query Swatches { @@ -194,10 +200,55 @@ query Swatches { The `hex` field accepts a `withAlpha` argument (default `true`), and `contrastRatio` requires a `with` argument containing the hex value of the color to compare against. +### Saving Color Field Data + +In an element or [entry form](kb:entry-form) that includes a color field, you can use the browser’s native `color` input type to submit a custom value: + +```twig +{{ input('color', 'fields[myColorField]', entry.myColorField) }} + +{# ...equivalent to... #} + + +``` + +If you cannot use a `color` input (or you are deriving or submitting the value in some other way), Craft can normalize a wide variety of [hexadecimal values](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/hex-color): + +- `f00` → `#ff0000` (a `#` is prepended to all values, if not present) +- `#f00` → `#ff0000` (expanding three-value syntax to six) +- `#f004` → `#ff000044` (expanding four-value syntax to eight, including alpha) +- `#ff0000ff` → `#ff0000` (removing alpha channel for fully-opaque colors) + ::: tip -Refer to the [`ColorData`](craft5:craft\fields\data\ColorData) class reference for a complete list of methods! +To avoid ambiguity, it’s best to submit a six- or eight-character hexadecimal color, prepended with the `#` symbol. ::: +Most browsers’ color picker interfaces [do not present an alpha channel](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/color#html.elements.input.alpha), but you can enable it for those that do: + +```twig{4} +{% set colorField = craft.app.fields.getFieldByHandle('myColorField') %} + +{{ input('color', 'fields[myColorField]', entry.myColorField, { + alpha: colorField.allowCustomAlpha, +}) }} +``` + +Color fields that use a palette can be presented as a [dropdown](dropdown.md): + +```twig +{% set colorField = craft.app.fields.getFieldByHandle('myColorField') %} + + +``` + ## Querying by Color There are no special query features for color fields; refer to the [plain text](plain-text.md) field for a list of standard string comparison syntaxes. diff --git a/docs/5.x/reference/field-types/country.md b/docs/5.x/reference/field-types/country.md index 54710f9bb..8e4599ffd 100644 --- a/docs/5.x/reference/field-types/country.md +++ b/docs/5.x/reference/field-types/country.md @@ -50,7 +50,7 @@ In earlier versions of Craft, use the [address repository](../element-types/addr The `country` variable in this example is an instance of [`CommerceGuys\Addressing\Country\Country`](repo:commerceguys/addressing/blob/master/src/Country/Country.php). -#### Localization +### Localization To get the localized name of a country, you must re-fetch it from the address repository: diff --git a/docs/5.x/reference/twig/filters.md b/docs/5.x/reference/twig/filters.md index 5bf49b187..d5dd4ce15 100644 --- a/docs/5.x/reference/twig/filters.md +++ b/docs/5.x/reference/twig/filters.md @@ -742,7 +742,7 @@ Hashes from other algorithms can be checked by re-computing the them from your t #### Arguments -`algo` +`algo` : The hashing algorithm to use. Check the output of the `hash_algos()` function on your system for a complete list of supported algorithms. diff --git a/docs/5.x/system/control-panel.md b/docs/5.x/system/control-panel.md index e36d2d36c..82f079c6b 100644 --- a/docs/5.x/system/control-panel.md +++ b/docs/5.x/system/control-panel.md @@ -160,9 +160,9 @@ We recommend that this is enabled [only in development environments](../deploy.m caption="The settings screen in Craft Pro. Some tiles may only be available in paid editions of Craft." id="settings" :poi="{ - system: [80, 24], - content: [80, 46], - extend: [80, 68], + system: [85, 24], + content: [85, 46], + extend: [85, 68], }"> Craft settings screen @@ -195,7 +195,8 @@ Group | Description **Tags** | Define simple taxonomies with [tag groups](../reference/element-types/tags.md#tag-groups). ::: tip -Prior to Craft 5.12, asset settings and filesystems had their own tiles in a separate “Media” section. Filesystems are now managed from . +Prior to Craft 5.12, asset settings and filesystems had their own tiles in a separate “Media” section. +Filesystems are now managed from . ::: #### Extend @@ -209,11 +210,13 @@ Group | Description #### Plugins -Additional tiles may appear in a final group, after installing [plugins](plugins.md). Plugin settings are also accessible via the screen. +Installed [plugins](plugins.md) may add more tiles to a final **Plugins** group. +Plugins’ settings screens are also accessible via their rows in . #### Read-Only Mode -[Administrators](user-management.md#admin-accounts) can always _view_ settings. A banner appears at the top of each settings screen, and inputs and controls are simplified or disabled: +[Administrators](user-management.md#admin-accounts) can always _view_ settings. +A banner appears at the top of each settings screen, and inputs and controls are simplified or disabled: