From 365b91e1b43a6762ff1d1163e175ebbe181fcbac Mon Sep 17 00:00:00 2001 From: Jessica Boezwinkle Date: Wed, 5 Jul 2023 16:51:27 +0100 Subject: [PATCH 01/12] docs: misc cloud updates --- docs/cloud/configuration.mdx | 19 ++++++++++++++----- docs/cloud/creating-a-project.mdx | 18 ++++++++++-------- docs/cloud/projects.mdx | 4 ++-- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/cloud/configuration.mdx b/docs/cloud/configuration.mdx index cea2c9d6e9..450236004f 100644 --- a/docs/cloud/configuration.mdx +++ b/docs/cloud/configuration.mdx @@ -10,11 +10,16 @@ keywords: configuration, config, settings, project, cloud, payload cloud, deploy Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you. -Note: All Payload Cloud teams that deploy a project require a card on file. This helps us prevent fraud and abuse on our platform. If you select a plan with a free trial, You will not be charged until your trial period is over. We’ll remind you 7 days before your trial ends and you can cancel anytime. + + Note: All Payload Cloud teams that deploy a project require a card on file. + This helps us prevent fraud and abuse on our platform. If you select a plan + with a free trial, You will not be charged until your trial period is over. + We’ll remind you 7 days before your trial ends and you can cancel anytime. + ### Project Details -| Field | Description | +| Option | Description | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Region** | Select the region closest to your audience. This will ensure the fastest communication between your data and your client. | | **Project Name** | A name for your project. You can change this at any time. | @@ -25,7 +30,7 @@ Note: All Payload Cloud teams that deploy a project require a card on file. This If you are deploying a new project from a template, the following settings will all be automatically configured for you. If you are using your own repository, you will need to make sure your build settings are accurate for your project to deploy correctly. -| Field | Description | +| Option | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Root Directory** | The folder where your `package.json` file lives. | | **Install Command** | The command used to install your modules, for example: `yarn install` or `npm install` | @@ -38,11 +43,15 @@ If you are deploying a new project from a template, the following settings will Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here. -Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed with `PAYLOAD_PUBLIC_`. [Learn more](http://local.payloadcms.com:3000/docs/admin/webpack#admin-environment-vars). + + Note: For security reasons, any variables you wish to provide to the Admin + panel must be prefixed with `PAYLOAD_PUBLIC_`.  Learn more + [here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars). + ### Payment -Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. You can modify your payment methods in your Project settings as well as Team settings. +Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings. Note: All Payload Cloud teams that deploy a project require a diff --git a/docs/cloud/creating-a-project.mdx b/docs/cloud/creating-a-project.mdx index 7eea240a06..1c56603bba 100644 --- a/docs/cloud/creating-a-project.mdx +++ b/docs/cloud/creating-a-project.mdx @@ -6,19 +6,21 @@ desc: Get started with Payload Cloud, a deployment solution specifically designe keywords: cloud, hosted, database, storage, email, deployment, serverless, node, mongodb, s3, aws, cloudflare, atlas, resend, payload, cms --- -Payload Cloud is a deployment solution specifically designed for Node + MongoDB applications, offering seamless deployment of your entire stack in one place. You can get started in minutes with a one-click template, or bring your own codebase with you. Payload Cloud offers different plans tailored to meet your specific needs, including a MongoDB Atlas database, S3 file storage, and email delivery powered by Resend. To see the full breakdown of features and plans, see our [Cloud Pricing page](https://payloadcms.com/cloud-pricing). +A deployment solution specifically designed for Node + MongoDB applications, offering seamless deployment of your entire stack in one place. You can get started in minutes with a one-click template, or bring your own codebase with you. -To get started with Payload Cloud, you will first need to create an account. From the login screen, select **Register for Free**. Once you’ve created an account, you’ll need to verify it by visiting the link sent to your email. After that, you can log into your account and get started. +Payload Cloud offers different plans tailored to meet your specific needs, including a MongoDB Atlas database, S3 file storage, and email delivery powered by Resend. To see the full breakdown of features and plans, see our [Cloud Pricing page](https://payloadcms.com/cloud-pricing). + +To get started, you will first need to create an account. Head over to [the login screen](https://payloadcms.com/login) and **Register for Free**. - To create your first project, you can either [start from a + To create your first project, you can either select [a template](#starting-from-a-template), or [import an existing - project](#importing-from-an-existing-codebase) from your GitHub account. + project](#importing-from-an-existing-codebase) from GitHub. ## Starting from a Template -Payload Cloud templates provide a one-click, simple solution to quickly deploy a new application. Templates come pre-configured, so there is no additional set up needed to run your application on Payload Cloud. +Templates come preconfigured and provide a one-click, simple solution to quickly deploy a new application. ![Screen for creating a new project from a template](https://payloadcms.com/images/docs/cloud/create-from-template.jpg) _Creating a new project from a template._ @@ -41,11 +43,11 @@ Once you are ready, click **Create Project**. This will clone the selected templ Payload Cloud works for any Node + MongoDB app. From the New Project page, select **import an existing Git codebase**. Choose the organization and select the repository you want to import. From here, you will be taken to the configuration page to set up your project for deployment. +![Screen for creating a new project from an existing repository](https://payloadcms.com/images/docs/cloud/create-from-existing.jpg) +_Creating a new project from an existing repository._ + Note: In order to make use of the features of Payload Cloud in your own codebase, you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your Payload app. - -![Screen for creating a new project from an existing repository](https://payloadcms.com/images/docs/cloud/create-from-existing.jpg) -_Creating a new project from an existing repository._ diff --git a/docs/cloud/projects.mdx b/docs/cloud/projects.mdx index cd2a21a4f7..efb9289a8a 100644 --- a/docs/cloud/projects.mdx +++ b/docs/cloud/projects.mdx @@ -41,8 +41,8 @@ From the Environment Variables page of the Settings tab, you can add, update and Note: For security reasons, any variables you wish to provide to the Admin - panel must be prefixed with `PAYLOAD_PUBLIC_`. [Learn - more](http://local.payloadcms.com:3000/docs/admin/webpack#admin-environment-vars). + panel must be prefixed with `PAYLOAD_PUBLIC_`.  Learn more + [here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars). ### Custom Domains From 868823d9e149b361089594518c93ede873784b2d Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Wed, 5 Jul 2023 11:58:07 -0400 Subject: [PATCH 02/12] chore(templates): change user collection to default access (#2972) --- templates/blank/src/collections/Users.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/templates/blank/src/collections/Users.ts b/templates/blank/src/collections/Users.ts index e5d661c9ae..93b938c2b9 100644 --- a/templates/blank/src/collections/Users.ts +++ b/templates/blank/src/collections/Users.ts @@ -6,9 +6,6 @@ const Users: CollectionConfig = { admin: { useAsTitle: 'email', }, - access: { - read: () => true, - }, fields: [ // Email added by default // Add more fields as needed From 03c2b36b849d3b05d521e143a7032cba3ab08122 Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Wed, 5 Jul 2023 11:58:19 -0400 Subject: [PATCH 03/12] chore: move cross-env to runtime dep (#2970) --- templates/blank/package.json | 2 +- templates/ecommerce/package.json | 2 +- templates/website/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/blank/package.json b/templates/blank/package.json index 25a53962b2..9a8bb49530 100644 --- a/templates/blank/package.json +++ b/templates/blank/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@payloadcms/plugin-cloud": "^0.0.10", + "cross-env": "^7.0.3", "dotenv": "^8.2.0", "express": "^4.17.1", "payload": "^1.7.2" @@ -23,7 +24,6 @@ "devDependencies": { "@types/express": "^4.17.9", "copyfiles": "^2.4.1", - "cross-env": "^7.0.3", "nodemon": "^2.0.6", "ts-node": "^9.1.1", "typescript": "^4.8.4" diff --git a/templates/ecommerce/package.json b/templates/ecommerce/package.json index 2871986262..1e493dc9a7 100644 --- a/templates/ecommerce/package.json +++ b/templates/ecommerce/package.json @@ -30,6 +30,7 @@ "@stripe/react-stripe-js": "^1.16.3", "@stripe/stripe-js": "^1.46.0", "apollo-link-http": "^1.5.17", + "cross-env": "^7.0.3", "dotenv": "^8.2.0", "eslint-import-resolver-alias": "^1.1.2", "express": "^4.17.1", @@ -50,7 +51,6 @@ "@typescript-eslint/eslint-plugin": "^5.51.0", "@typescript-eslint/parser": "^5.51.0", "copyfiles": "^2.4.1", - "cross-env": "^7.0.3", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-filenames": "^1.3.2", diff --git a/templates/website/package.json b/templates/website/package.json index 13a360fd34..5adcc6bebe 100644 --- a/templates/website/package.json +++ b/templates/website/package.json @@ -23,6 +23,7 @@ "@payloadcms/plugin-nested-docs": "^1.0.4", "@payloadcms/plugin-redirects": "^1.0.0", "@payloadcms/plugin-seo": "^1.0.10", + "cross-env": "^7.0.3", "dotenv": "^8.2.0", "express": "^4.17.1", "payload": "^1.7.2" @@ -35,7 +36,6 @@ "@typescript-eslint/eslint-plugin": "^5.51.0", "@typescript-eslint/parser": "^5.51.0", "copyfiles": "^2.4.1", - "cross-env": "^7.0.3", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-filenames": "^1.3.2", From 9b22c4b654b75fad724182478a2fd80879418215 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 5 Jul 2023 12:02:56 -0400 Subject: [PATCH 04/12] docs: fixes misc links (#2971) --- docs/admin/components.mdx | 35 ++++++++------ docs/admin/overview.mdx | 6 +-- docs/configuration/i18n.mdx | 56 ++++++++++++--------- docs/configuration/overview.mdx | 5 +- docs/email/overview.mdx | 86 ++++++++++++++++++--------------- docs/fields/relationship.mdx | 8 +-- docs/fields/upload.mdx | 74 ++++++++++++++++------------ docs/graphql/overview.mdx | 8 ++- docs/production/deployment.mdx | 37 ++++++++++---- docs/queries/overview.mdx | 14 ++++-- 10 files changed, 191 insertions(+), 138 deletions(-) diff --git a/docs/admin/components.mdx b/docs/admin/components.mdx index d281719517..e934dfe7b9 100644 --- a/docs/admin/components.mdx +++ b/docs/admin/components.mdx @@ -85,14 +85,15 @@ You can override components on a Collection-by-Collection basis via each Collect | **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. | | **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. | | **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. | -| **`BeforeList`** | Array of components to inject _before_ the built-in List view - | -| **`BeforeListTable`** | Array of components to inject _before_ the built-in List view's table - | -| **`AfterListTable`** | Array of components to inject _after_ the built-in List view's table - | -| **`AfterList`** | Array of components to inject _after_ the built-in List view - | +| **`BeforeList`** | Array of components to inject _before_ the built-in List view | +| | +| **`BeforeListTable`** | Array of components to inject _before_ the built-in List view's table | +| | +| **`AfterListTable`** | Array of components to inject _after_ the built-in List view's table | +| | +| **`AfterList`** | Array of components to inject _after_ the built-in List view | +| | + #### Examples ```tsx @@ -146,6 +147,7 @@ export const CustomPreviewButton: CustomPreviewButtonProps = ({ ##### Custom Collection List View Example Collection.ts + ```tsx import { MyListComponent } from './MyListComponent'; export const MyCollection: CollectionConfig = { @@ -164,12 +166,16 @@ export const MyCollection: CollectionConfig = { ``` MyListComponent.tsx + ```tsx -import React from 'react'; -import {List, type Props} from 'payload/components/views/List' // Payload's default List view component and its props +import React from "react"; +import { List, type Props } from "payload/components/views/List"; // Payload's default List view component and its props export const MyListComponent: React.FC = (props) => (
-

Some text before the default list view component. If you just want to do that, you can also use the admin.components.list.BeforeList hook

+

+ Some text before the default list view component. If you just want to do + that, you can also use the admin.components.list.BeforeList hook +

); @@ -260,11 +266,8 @@ const CustomTextField: React.FC = ({ path }) => { For more information regarding the hooks that are available to you while you - build custom components, including the useField hook,{" "} - - click here - - . + build custom components, including the useField hook, [click + here](/docs/admin/hooks). ## Custom routes diff --git a/docs/admin/overview.mdx b/docs/admin/overview.mdx index d475f49498..f3da913beb 100644 --- a/docs/admin/overview.mdx +++ b/docs/admin/overview.mdx @@ -25,7 +25,7 @@ _Screenshot of the Admin panel while editing a document from an example `AllFiel All options for the Admin panel are defined in your base Payload config file. | Option | Description | -| --------------------- | -------------------------------------------------------------------------------------------------- | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --- | | `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) | | `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. | | `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. | @@ -45,8 +45,8 @@ All options for the Admin panel are defined in your base Payload config file. Important:
- The Payload Admin panel can only be used by one Collection that supports{" "} - Authentication. + The Payload Admin panel can only be used by one Collection that supports + [Authentication](/docs/authentication/overview).
To specify which Collection to use to log in to the Admin panel, pass the `admin` options a `user` key equal to the slug of the Collection that you'd like to use. diff --git a/docs/configuration/i18n.mdx b/docs/configuration/i18n.mdx index df34bdbc2b..7cf562a7cb 100644 --- a/docs/configuration/i18n.mdx +++ b/docs/configuration/i18n.mdx @@ -11,44 +11,49 @@ Not only does Payload support managing localized content, it also has internatio While Payload's built-in features come translated, you may want to also translate parts of your project's configuration too. This is possible in places like collections and globals labels and groups, field labels, descriptions and input placeholder text. The admin UI will display all the correct translations you provide based on the user's language. Here is an example of a simple collection supporting both English and Spanish editors: + ```ts -import { CollectionConfig } from 'payload/types'; +import { CollectionConfig } from "payload/types"; export const Articles: CollectionConfig = { - slug: 'articles', + slug: "articles", labels: { singular: { - en: 'Article', es: 'Artículo', + en: "Article", + es: "Artículo", }, plural: { - en: 'Articles', es: 'Artículos', + en: "Articles", + es: "Artículos", }, }, admin: { - group: { en: 'Content', es: 'Contenido' }, + group: { en: "Content", es: "Contenido" }, }, fields: [ { - name: 'title', - type: 'text', + name: "title", + type: "text", label: { - en: 'Title', es: 'Título', + en: "Title", + es: "Título", }, admin: { - placeholder: { en: 'Enter title', es: 'Introduce el título' } - } + placeholder: { en: "Enter title", es: "Introduce el título" }, + }, }, { - name: 'type', - type: 'radio', - options: [{ - value: 'news', - label: { en: 'News', es: 'Noticias' }, - }, // etc... + name: "type", + type: "radio", + options: [ + { + value: "news", + label: { en: "News", es: "Noticias" }, + }, // etc... ], }, ], -} +}; ``` ### Admin UI @@ -57,8 +62,10 @@ The Payload admin panel reads the language settings of a user's browser and disp After a user logs in, they can change their language selection in the `/account` view. - Note:
- If there is a language that Payload does not yet support, we accept code contributions. + Note: +
+ If there is a language that Payload does not yet support, we accept code + [contributions](https://github.com/payloadcms/payload/blob/master/contributing.md).
### Node Express @@ -76,21 +83,22 @@ In your Payload config, you can add translations and customize the settings in ` **Example Payload config extending i18n:** ```ts -import { buildConfig } from 'payload/config' +import { buildConfig } from "payload/config"; export default buildConfig({ //... i18n: { - fallbackLng: 'en', // default + fallbackLng: "en", // default debug: false, // default resources: { en: { - custom: { // namespace can be anything you want - key1: 'Translation with {{variable}}', // translation + custom: { + // namespace can be anything you want + key1: "Translation with {{variable}}", // translation }, // override existing translation keys general: { - dashboard: 'Home', + dashboard: "Home", }, }, }, diff --git a/docs/configuration/overview.mdx b/docs/configuration/overview.mdx index 0c7db48dca..cd61e4b839 100644 --- a/docs/configuration/overview.mdx +++ b/docs/configuration/overview.mdx @@ -122,9 +122,8 @@ project-name
If you use an environment variable to configure any properties that are required for the Admin panel to function (ex. serverURL or any routes), you - need to make sure that your Admin panel code can access it.{" "} - Click here for more - info. + need to make sure that your Admin panel code can access it. [Click + here](/docs/admin/webpack#admin-environment-vars) for more info.
### Customizing & overriding the config location diff --git a/docs/email/overview.mdx b/docs/email/overview.mdx index b973ff56c5..d6bc406a88 100644 --- a/docs/email/overview.mdx +++ b/docs/email/overview.mdx @@ -25,21 +25,22 @@ in the `email` property object of your payload init call. Payload will make use The following options are configurable in the `email` property object as part of the options object when calling payload.init(). -| Option | Description | -| ---------------------------- | -------------| -| **`fromName`** * | The name part of the From field that will be seen on the delivered email | -| **`fromAddress`** * | The email address part of the From field that will be used when delivering email | -| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set | -| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com/smtp/) or see the examples below | -| **`logMockCredentials`** | If set to true and no transport/transportOptions, ethereal credentials will be logged to console on startup | +| Option | Description | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`fromName`** \* | The name part of the From field that will be seen on the delivered email | +| **`fromAddress`** \* | The email address part of the From field that will be used when delivering email | +| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set | +| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com/smtp/) or see the examples below | +| **`logMockCredentials`** | If set to true and no transport/transportOptions, ethereal credentials will be logged to console on startup | -*\* An asterisk denotes that a property is required.* +_\* An asterisk denotes that a property is required._ ### Use SMTP Simple Mail Transfer Protocol, also known as SMTP can be passed in using the `transportOptions` object on the `email` options. **Example email part using SMTP:** + ```ts payload.init({ email: { @@ -47,24 +48,26 @@ payload.init({ host: process.env.SMTP_HOST, auth: { user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS + pass: process.env.SMTP_PASS, }, port: 587, secure: true, // use TLS tls: { // do not fail on invalid certs - rejectUnauthorized: false - } + rejectUnauthorized: false, }, - fromName: 'hello', - fromAddress: 'hello@example.com' - } + }, + fromName: "hello", + fromAddress: "hello@example.com", + }, // ... -}) +}); ``` - It is best practice to avoid saving credentials or API keys directly in your code, use environment variables. + It is best practice to avoid saving credentials or API keys directly in your + code, use [environment + variables](/docs/configuration/overview#using-environment-variables-in-your-config). ### Use an email service @@ -72,57 +75,62 @@ payload.init({ Many third party mail providers are available and offer benefits beyond basic SMTP. As an example your payload init could look this if you wanted to use SendGrid.com though the same approach would work for any other [NodeMailer transports](https://nodemailer.com/transports/) shown here or provided by another third party. ```ts -import payload from 'payload' -import nodemailerSendgrid from 'nodemailer-sendgrid' +import payload from "payload"; +import nodemailerSendgrid from "nodemailer-sendgrid"; const sendGridAPIKey = process.env.SENDGRID_API_KEY; payload.init({ - ...sendGridAPIKey ? { - email: { - transportOptions: nodemailerSendgrid({ - apiKey: sendGridAPIKey, - }), - fromName: 'Admin', - fromAddress: 'admin@example.com', - }, - } : {}, + ...(sendGridAPIKey + ? { + email: { + transportOptions: nodemailerSendgrid({ + apiKey: sendGridAPIKey, + }), + fromName: "Admin", + fromAddress: "admin@example.com", + }, + } + : {}), }); ``` ### Use a custom NodeMailer transport + To take full control of the mail transport you may wish to use `nodemailer.createTransport()` on your server and provide it to Payload init. ```ts -import payload from 'payload' -import nodemailer from 'nodemailer' +import payload from "payload"; +import nodemailer from "nodemailer"; -const payload = require('payload'); -const nodemailer = require('nodemailer'); +const payload = require("payload"); +const nodemailer = require("nodemailer"); const transport = await nodemailer.createTransport({ host: process.env.SMTP_HOST, port: 587, auth: { user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS + pass: process.env.SMTP_PASS, }, }); payload.init({ email: { - fromName: 'Admin', - fromAddress: 'admin@example.com', - transport + fromName: "Admin", + fromAddress: "admin@example.com", + transport, }, // ... }); ``` ### Sending Mail + With a working transport you can call it anywhere you have access to payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `email` or `text` for the email being sent. To see all available message configuration options see [NodeMailer](https://nodemailer.com/message). ### Mock transport + By default, Payload uses a mock implementation that only sends mail to the [ethereal](https://ethereal.email) capture service that will never reach a user's inbox. While in development you may wish to make use of the captured messages which is why the payload output during server output helpfully logs this out on the server console. To see ethereal credentials, add `logMockCredentials: true` to the email options. This will cause them to be logged to console on startup. @@ -130,8 +138,8 @@ To see ethereal credentials, add `logMockCredentials: true` to the email options ```ts payload.init({ email: { - fromName: 'Admin', - fromAddress: 'admin@example.com', + fromName: "Admin", + fromAddress: "admin@example.com", logMockCredentials: true, // Optional }, // ... @@ -139,6 +147,7 @@ payload.init({ ``` **Console output when starting payload with a mock email instance and logMockCredentials: true** + ``` [06:37:21] INFO (payload): Starting Payload... [06:37:22] INFO (payload): Payload Demo Initialized @@ -153,7 +162,8 @@ payload.init({ The mock email handler is used when payload is started with neither `transport` or `transportOptions` to know how to deliver email. - The randomly generated email account username and password will be different each time the Payload server starts. + The randomly generated email account username and password will be different + each time the Payload server starts. ### Using multiple mail providers diff --git a/docs/fields/relationship.mdx b/docs/fields/relationship.mdx index bb7975e385..976b1d2d44 100644 --- a/docs/fields/relationship.mdx +++ b/docs/fields/relationship.mdx @@ -25,8 +25,8 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma | **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. | | **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). | | **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. | -| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. | -| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. | +| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. | +| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. | | **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](/docs/getting-started/concepts#depth) | | **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. | | **`unique`** | Enforce that each entry in the Collection has a unique value for this field. | @@ -47,8 +47,8 @@ _\* An asterisk denotes that a property is required._ Tip:
- The Depth parameter can be - used to automatically populate related documents that are returned by the API. + The [Depth](/docs/getting-started/concepts#depth) parameter can be used to + automatically populate related documents that are returned by the API.
### Admin config diff --git a/docs/fields/upload.mdx b/docs/fields/upload.mdx index 4a593caf95..65ba7025fd 100644 --- a/docs/fields/upload.mdx +++ b/docs/fields/upload.mdx @@ -6,13 +6,18 @@ desc: Upload fields will allow a file to be uploaded, only from a collection sup keywords: upload, images media, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express --- - - The Upload field allows for the selection of a Document from a collection supporting Uploads, and formats the selection as a thumbnail in the Admin panel. + + The Upload field allows for the selection of a Document from a collection + supporting Uploads, and formats the selection as a thumbnail in the Admin + panel. - Important:
- To use this field, you need to have a Collection configured to allow Uploads. For more information, click here to read about how to enable Uploads on a collection by collection basis. + Important: +
+ To use this field, you need to have a Collection configured to allow Uploads. + For more information, [click here](/docs/upload/overview) to read about how to + enable Uploads on a collection by collection basis.
**Example uses:** @@ -23,10 +28,10 @@ keywords: upload, images media, fields, config, configuration, documentation, Co ### Config -| Option | Description | -| ---------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **`name`** * | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) | -| **`*relationTo`** * | Provide a single collection `slug` to allow this field to accept a relation to. Note: the related collection must be configured to support Uploads. | +| Option | Description | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) | +| **`*relationTo`** \* | Provide a single collection `slug` to allow this field to accept a relation to. Note: the related collection must be configured to support Uploads. | | **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-upload-options). | | **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](/docs/getting-started/concepts#depth) | | **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. | @@ -41,28 +46,28 @@ keywords: upload, images media, fields, config, configuration, documentation, Co | **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. | | **`required`** | Require this field to have a value. | | **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. | -| **`custom`** | Extension point for adding custom data (e.g. for plugins) | +| **`custom`** | Extension point for adding custom data (e.g. for plugins) | -*\* An asterisk denotes that a property is required.* +_\* An asterisk denotes that a property is required._ ### Example `collections/ExampleCollection.ts` + ```ts -import { CollectionConfig } from 'payload/types'; +import { CollectionConfig } from "payload/types"; export const ExampleCollection: CollectionConfig = { - slug: 'example-collection', + slug: "example-collection", fields: [ { - name: 'backgroundImage', // required - type: 'upload', // required - relationTo: 'media', // required + name: "backgroundImage", // required + type: "upload", // required + relationTo: "media", // required required: true, - } - ] -} - + }, + ], +}; ``` ### Filtering upload options @@ -71,23 +76,23 @@ Options can be dynamically limited by supplying a [query constraint](/docs/queri The `filterOptions` property can either be a `Where` query directly, or a function that returns one. When using a function, it will be called with an argument object with the following properties: -| Property | Description | -| ------------- | -------------| -| `relationTo` | The `relationTo` to filter against (as defined on the field) | -| `data` | An object of the full collection or global document currently being edited | -| `siblingData` | An object of the document data limited to fields within the same parent to the field | -| `id` | The value of the collection `id`, will be `undefined` on create request | -| `user` | The currently authenticated user object | +| Property | Description | +| ------------- | ------------------------------------------------------------------------------------ | +| `relationTo` | The `relationTo` to filter against (as defined on the field) | +| `data` | An object of the full collection or global document currently being edited | +| `siblingData` | An object of the document data limited to fields within the same parent to the field | +| `id` | The value of the collection `id`, will be `undefined` on create request | +| `user` | The currently authenticated user object | **Example:** ```ts const uploadField = { - name: 'image', - type: 'upload', - relationTo: 'media', + name: "image", + type: "upload", + relationTo: "media", filterOptions: { - mimeType: { contains: 'image' }, + mimeType: { contains: "image" }, }, }; ``` @@ -95,6 +100,11 @@ const uploadField = { You can learn more about writing queries [here](/docs/queries/overview). - Note:
- When an upload field has both filterOptions and a custom validate function, the api will not validate filterOptions unless you call the default upload field validation function imported from payload/fields/validations in your validate function. + Note: +
+ When an upload field has both filterOptions and a custom{" "} + validate function, the api will not validate{" "} + filterOptions unless you call the default upload field + validation function imported from payload/fields/validations{" "} + in your validate function.
diff --git a/docs/graphql/overview.mdx b/docs/graphql/overview.mdx index d74c751af3..5d34b174bb 100644 --- a/docs/graphql/overview.mdx +++ b/docs/graphql/overview.mdx @@ -118,11 +118,9 @@ You can even log in using the `login[collection-singular-label-here]` mutation t Tip:
To see more regarding how the above queries and mutations are used, visit your - GraphQL playground (by default at{" "} - - (http://localhost:3000/api/graphql-playground - - ) while your server is running. There, you can use the "Schema" and "Docs" + GraphQL playground (by default at + [http://localhost:3000/api/graphql-playground](http://localhost:3000/api/graphql-playground)) + while your server is running. There, you can use the "Schema" and "Docs" buttons on the right to see a ton of detail about how GraphQL operates within Payload.
diff --git a/docs/production/deployment.mdx b/docs/production/deployment.mdx index 06f799d6b6..bd5ef5bb46 100644 --- a/docs/production/deployment.mdx +++ b/docs/production/deployment.mdx @@ -7,7 +7,9 @@ keywords: deployment, production, config, configuration, documentation, Content --- - So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to launch. Awesome! Great work! Now, what's next? + So you've developed a Payload app, it's fully tested, and running great + locally. Now it's time to launch. Awesome! Great work! Now, + what's next? There are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need to consider these main aspects: @@ -35,7 +37,13 @@ When you initialize Payload, you provide it with a `secret` property. This prope Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you wield that power responsibly before deploying to Production. - By default, all Access Control functions require that a user is successfully logged in to Payload to create, read, update, or delete data. But, if you allow public user registration, for example, you will want to make sure that your access control functions are more strict - permitting only appropriate users to perform appropriate actions. + + By default, all Access Control functions require that a user is successfully + logged in to Payload to create, read, update, or delete data. + {" "} + But, if you allow public user registration, for example, you will want to make + sure that your access control functions are more strict - permitting{" "} + only appropriate users to perform appropriate actions. ##### Building the Admin panel @@ -43,6 +51,7 @@ Because _**you**_ are in complete control of who can do what with your data, you Before running in Production, you need to have built a production-ready copy of the Payload Admin panel. To do this, Payload provides the `build` NPM script. You can use it by adding a `script` to your `package.json` file like this: `package.json`: + ```json { "name": "project-name-here", @@ -51,7 +60,8 @@ Before running in Production, you need to have built a production-ready copy of }, "dependencies": { // your dependencies - }, + } +} ``` Then, to build Payload, you would run `npm run build` in your project folder. A production-ready Admin bundle will be created in the `build` directory. @@ -83,14 +93,19 @@ Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas Note:
- If versions are enabled and a collection has many documents you may need a minimum of an m10 mongoDB atlas cluster if you reach a sorting `exceeded memory limit` error to view a collection list in the admin UI. The limitations of the m2 and m5 tier clusters are here: - Atlas M0 (Free Cluster), M2, and M5 Limitations + If versions are enabled and a collection has many documents you may need a + minimum of an m10 mongoDB atlas cluster if you reach a sorting `exceeded + memory limit` error to view a collection list in the admin UI. The limitations + of the m2 and m5 tier clusters are here: [Atlas M0 (Free Cluster), M2, and M5 + Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
##### DocumentDB + When using AWS DocumentDB, you will need to configure connection options for authentication in the `mongoOptions` passed to `payload.init`. You also need to set `mongoOptions.useFacet` to `false` to disable use of the unsupported `$facet` aggregation. ##### CosmosDB + When using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all fields that may be sorted in the admin UI use the indexSortableFields configuration option. ## File storage @@ -116,15 +131,18 @@ Alternatively, persistent filesystems will never delete your files and can be tr - Many other more traditional web hosts - Warning:
- If you rely on Payload's Upload functionality, make sure you either use a host with a persistent filesystem or have an integration with a third-party file host like Amazon S3. + Warning: +
+ If you rely on Payload's Upload functionality, make sure you + either use a host with a persistent filesystem or have an integration with a + third-party file host like Amazon S3.
##### Using ephemeral filesystem providers like Heroku If you don't use Payload's `upload` functionality, you can go ahead and use Heroku or similar platform easily. Everything will work exactly as you want it to. -But, if you do, and you still want to use an ephemeral filesystem provider, you can write a hook-based solution to *copy* the files your users upload to a more permanent storage solution like Amazon S3 or DigitalOcean Spaces. +But, if you do, and you still want to use an ephemeral filesystem provider, you can write a hook-based solution to _copy_ the files your users upload to a more permanent storage solution like Amazon S3 or DigitalOcean Spaces. **To automatically send uploaded files to S3 or similar, you could:** @@ -182,10 +200,9 @@ CMD ["node", "dist/server.js"] Here is an example of a docker-compose.yml file that can be used for development ```yml -version: '3' +version: "3" services: - payload: image: node:18-alpine ports: diff --git a/docs/queries/overview.mdx b/docs/queries/overview.mdx index 16657d587f..f42b970583 100644 --- a/docs/queries/overview.mdx +++ b/docs/queries/overview.mdx @@ -9,7 +9,13 @@ keywords: query, documents, overview, documentation, Content Management System, Payload provides an extremely granular querying language through all APIs. Each API takes the same syntax and fully supports all options. - Here, "querying" relates to filtering or searching through documents within a Collection. You can build queries to pass to Find operations as well as to restrict which documents certain users can access via access control functions. + + Here, "querying" relates to filtering or searching through documents within + a Collection. + {" "} + You can build queries to pass to Find operations as well as to [restrict which + documents certain users can access](/docs/access-control/overview) via access + control functions. ### Simple queries @@ -67,8 +73,10 @@ The above example demonstrates a simple query but you can get much more complex. | `near` | For distance related to a [point field](/docs/fields/point) comma separated as `, , , `. | - Tip:
- If you know your users will be querying on certain fields a lot, you can add index: true to a field's config which will speed up searches using that field immensely. + Tip:
+ If you know your users will be querying on certain fields a lot, you can add + index: true + to a field's config which will speed up searches using that field immensely.
### And / Or Logic From b734a1c422d200cad1085b7e92f8540df4238e32 Mon Sep 17 00:00:00 2001 From: Alessio Gravili <70709113+AlessioGr@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:20:56 +0200 Subject: [PATCH 05/12] feat: narrow endpoint.method type (#1880) Co-authored-by: Jarrod Flesch --- src/collections/buildEndpoints.ts | 20 +++++++++++--------- src/config/types.ts | 3 +-- src/globals/buildEndpoints.ts | 10 +++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/collections/buildEndpoints.ts b/src/collections/buildEndpoints.ts index ab53035dfa..7790b66dba 100644 --- a/src/collections/buildEndpoints.ts +++ b/src/collections/buildEndpoints.ts @@ -23,7 +23,7 @@ import docAccessRequestHandler from './requestHandlers/docAccess'; import deleteByID from './requestHandlers/deleteByID'; const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { - let { endpoints } = collection; + const endpoints = [...collection.endpoints]; if (collection.auth) { if (!collection.auth.disableLocalStrategy) { @@ -43,7 +43,7 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { }); } - endpoints = endpoints.concat([ + endpoints.push( { path: '/login', method: 'post', @@ -64,10 +64,10 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { method: 'post', handler: resetPassword, }, - ]); + ); } - endpoints = endpoints.concat([ + endpoints.push( { path: '/init', method: 'get', @@ -88,11 +88,11 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { method: 'post', handler: refreshHandler, }, - ]); + ); } if (collection.versions) { - endpoints = endpoints.concat([ + endpoints.push( { path: '/versions', method: 'get', @@ -108,10 +108,10 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { method: 'post', handler: restoreVersion, }, - ]); + ); } - return endpoints.concat([ + endpoints.push( { path: '/', method: 'get', @@ -157,7 +157,9 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { method: 'delete', handler: deleteHandler, }, - ]); + ); + + return endpoints; }; export default buildEndpoints; diff --git a/src/config/types.ts b/src/config/types.ts index 84b9937861..9ce9a20301 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -181,8 +181,7 @@ export type Endpoint = { | 'patch' | 'delete' | 'connect' - | 'options' - | string; + | 'options'; /** * Middleware that will be called when the path/method matches * diff --git a/src/globals/buildEndpoints.ts b/src/globals/buildEndpoints.ts index ffa80d62e5..d601b36244 100644 --- a/src/globals/buildEndpoints.ts +++ b/src/globals/buildEndpoints.ts @@ -8,10 +8,10 @@ import findOne from './requestHandlers/findOne'; import docAccessRequestHandler from './requestHandlers/docAccess'; const buildEndpoints = (global: SanitizedGlobalConfig): Endpoint[] => { - const { endpoints, slug } = global; + const endpoints = [...global.endpoints]; if (global.versions) { - endpoints.push(...[ + endpoints.push( { path: '/versions', method: 'get', @@ -27,10 +27,10 @@ const buildEndpoints = (global: SanitizedGlobalConfig): Endpoint[] => { method: 'post', handler: restoreVersion(global), }, - ]); + ); } - endpoints.push(...[ + endpoints.push( { path: '/access', method: 'get', @@ -46,7 +46,7 @@ const buildEndpoints = (global: SanitizedGlobalConfig): Endpoint[] => { method: 'post', handler: update(global), }, - ]); + ); return endpoints; }; From 7c47e4b0d3c63f6f7800daaf424935d6067ffcc4 Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Wed, 5 Jul 2023 13:39:29 -0400 Subject: [PATCH 06/12] feat: improve typing of ExtendableError and APIError (#2864) --- src/errors/APIError.ts | 15 ++++++++++----- src/errors/QueryError.ts | 2 +- src/errors/ValidationError.ts | 14 ++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/errors/APIError.ts b/src/errors/APIError.ts index 9bf1a5cf4c..39572ccc7d 100644 --- a/src/errors/APIError.ts +++ b/src/errors/APIError.ts @@ -1,16 +1,16 @@ /* eslint-disable max-classes-per-file */ import httpStatus from 'http-status'; -class ExtendableError extends Error { +class ExtendableError extends Error { status: number; - data: {[key: string]: unknown}; + data: TData; isPublic: boolean; isOperational: boolean; - constructor(message: string, status: number, data: { [key: string]: unknown }, isPublic: boolean) { + constructor(message: string, status: number, data: TData, isPublic: boolean) { super(message); this.name = this.constructor.name; this.message = message; @@ -26,7 +26,7 @@ class ExtendableError extends Error { * Class representing an API error. * @extends ExtendableError */ -class APIError extends ExtendableError { +class APIError extends ExtendableError { /** * Creates an API error. * @param {string} message - Error message. @@ -34,7 +34,12 @@ class APIError extends ExtendableError { * @param {object} data - response data to be returned. * @param {boolean} isPublic - Whether the message should be visible to user or not. */ - constructor(message: string, status: number = httpStatus.INTERNAL_SERVER_ERROR, data: any = null, isPublic = false) { + constructor( + message: string, + status: number = httpStatus.INTERNAL_SERVER_ERROR, + data: TData = null, + isPublic = false, + ) { super(message, status, data, isPublic); } } diff --git a/src/errors/QueryError.ts b/src/errors/QueryError.ts index 6d254267a3..0481070929 100644 --- a/src/errors/QueryError.ts +++ b/src/errors/QueryError.ts @@ -2,7 +2,7 @@ import httpStatus from 'http-status'; import type { TFunction } from 'i18next'; import APIError from './APIError'; -class QueryError extends APIError { +class QueryError extends APIError<{ path: string }[]> { constructor(results: { path: string }[], t?: TFunction) { const message = t ? t('error:unspecific', { count: results.length }) : `The following path${results.length === 1 ? '' : 's'} cannot be queried:`; super( diff --git a/src/errors/ValidationError.ts b/src/errors/ValidationError.ts index aa688461e1..66d3ed4fd5 100644 --- a/src/errors/ValidationError.ts +++ b/src/errors/ValidationError.ts @@ -2,14 +2,12 @@ import httpStatus from 'http-status'; import type { TFunction } from 'i18next'; import APIError from './APIError'; -class ValidationError extends APIError { - constructor(results: {message: string, field: string}[], t?: TFunction) { - const message = t ? t('error:followingFieldsInvalid', { count: results.length }) : `The following field${results.length === 1 ? ' is' : 's are'} invalid:`; - super( - `${message} ${results.map((f) => f.field).join(', ')}`, - httpStatus.BAD_REQUEST, - results, - ); +class ValidationError extends APIError<{ message: string; field: string }[]> { + constructor(results: { message: string; field: string }[], t?: TFunction) { + const message = t + ? t('error:followingFieldsInvalid', { count: results.length }) + : `The following field${results.length === 1 ? ' is' : 's are'} invalid:`; + super(`${message} ${results.map((f) => f.field).join(', ')}`, httpStatus.BAD_REQUEST, results); } } From 04851d0dc99e4a3df0a1ac642e9a4b9a3c06d8a1 Mon Sep 17 00:00:00 2001 From: Jessica Chowdhury <67977755+JessChowdhury@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:40:50 +0100 Subject: [PATCH 07/12] fix: ensures rows always have id's (#2968) Co-authored-by: Jarrod Flesch --- .../Form/buildStateFromSchema/addFieldStatePromise.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/admin/components/forms/Form/buildStateFromSchema/addFieldStatePromise.ts b/src/admin/components/forms/Form/buildStateFromSchema/addFieldStatePromise.ts index 507b6a9b62..84fda63313 100644 --- a/src/admin/components/forms/Form/buildStateFromSchema/addFieldStatePromise.ts +++ b/src/admin/components/forms/Form/buildStateFromSchema/addFieldStatePromise.ts @@ -84,9 +84,11 @@ export const addFieldStatePromise = async ({ const arrayValue = Array.isArray(valueWithDefault) ? valueWithDefault : []; const { promises, rowMetadata } = arrayValue.reduce((acc, row, i) => { const rowPath = `${path}${field.name}.${i}.`; + row.id = row?.id || new ObjectID().toHexString(); + state[`${rowPath}id`] = { value: row.id, - initialValue: row.id || new ObjectID().toHexString(), + initialValue: row.id, valid: true, }; @@ -149,9 +151,11 @@ export const addFieldStatePromise = async ({ const rowPath = `${path}${field.name}.${i}.`; if (block) { + row.id = row?.id || new ObjectID().toHexString(); + state[`${rowPath}id`] = { value: row.id, - initialValue: row.id || new ObjectID().toHexString(), + initialValue: row.id, valid: true, }; From 5b79067cc14874abbd1e1a5b6e619d41571b187f Mon Sep 17 00:00:00 2001 From: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:41:39 -0400 Subject: [PATCH 08/12] fix: ensures fields within blocks respect field level access control (#2969) --- .../forms/field-types/Blocks/index.tsx | 2 +- src/auth/types.ts | 13 +++++++--- src/utilities/getEntityPolicies.ts | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/admin/components/forms/field-types/Blocks/index.tsx b/src/admin/components/forms/field-types/Blocks/index.tsx index fbb12910ef..d7a0c1b2a4 100644 --- a/src/admin/components/forms/field-types/Blocks/index.tsx +++ b/src/admin/components/forms/field-types/Blocks/index.tsx @@ -273,7 +273,7 @@ const BlocksField: React.FC = (props) => { className={`${baseClass}__fields`} readOnly={readOnly} fieldTypes={fieldTypes} - permissions={permissions?.fields} + permissions={permissions?.blocks?.[blockType]?.fields} fieldSchema={blockToRender.fields.map((field) => ({ ...field, path: createNestedFieldPath(`${path}.${i}`, field), diff --git a/src/auth/types.ts b/src/auth/types.ts index a0f9c3254f..0e6ce06053 100644 --- a/src/auth/types.ts +++ b/src/auth/types.ts @@ -20,7 +20,14 @@ export type FieldPermissions = { permission: boolean } fields?: { - [field: string]: FieldPermissions + [fieldName: string]: FieldPermissions + } + blocks?: { + [blockSlug: string]: { + fields: { + [fieldName: string]: FieldPermissions + } + } } } @@ -31,7 +38,7 @@ export type CollectionPermission = { delete: Permission readVersions?: Permission fields: { - [field: string]: FieldPermissions + [fieldName: string]: FieldPermissions } } @@ -40,7 +47,7 @@ export type GlobalPermission = { update: Permission readVersions?: Permission fields: { - [field: string]: FieldPermissions + [fieldName: string]: FieldPermissions } } diff --git a/src/utilities/getEntityPolicies.ts b/src/utilities/getEntityPolicies.ts index 3a0ed6525e..a57b6de2f1 100644 --- a/src/utilities/getEntityPolicies.ts +++ b/src/utilities/getEntityPolicies.ts @@ -136,6 +136,7 @@ export async function getEntityPolicies(args: T): Promise(args: T): Promise { + if (!mutablePolicies[field.name].blocks?.[block.slug]) { + mutablePolicies[field.name].blocks[block.slug] = { + fields: {}, + [operation]: { permission: entityPermission }, + }; + } else if (!mutablePolicies[field.name].blocks[block.slug][operation]) { + mutablePolicies[field.name].blocks[block.slug][operation] = { permission: entityPermission }; + } + + await executeFieldPolicies({ + policiesObj: mutablePolicies[field.name].blocks[block.slug], + fields: block.fields, + operation, + entityPermission, + }); + })); + } } else if (field.fields) { await executeFieldPolicies({ policiesObj, @@ -158,6 +181,8 @@ export async function getEntityPolicies(args: T): Promise Date: Wed, 5 Jul 2023 15:11:46 -0400 Subject: [PATCH 09/12] fix: properly threads custom react-select props through relationship field (#2973) --- .../components/elements/ReactSelect/index.tsx | 3 +-- .../components/elements/ReactSelect/types.ts | 4 ++++ .../forms/field-types/Relationship/index.tsx | 2 +- .../select-components/MultiValueLabel/index.tsx | 4 ++-- test/fields/e2e.spec.ts | 17 +++++++++++------ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/admin/components/elements/ReactSelect/index.tsx b/src/admin/components/elements/ReactSelect/index.tsx index 5a9f989177..75cf284c5f 100644 --- a/src/admin/components/elements/ReactSelect/index.tsx +++ b/src/admin/components/elements/ReactSelect/index.tsx @@ -18,7 +18,6 @@ import type { Option } from './types'; import './index.scss'; - const createOption = (label: string) => ({ label, value: label, @@ -60,6 +59,7 @@ const SelectAdapter: React.FC = (props) => { isLoading={isLoading} placeholder={getTranslation(placeholder, i18n)} captureMenuScroll + customProps={selectProps} {...props} value={value} onChange={onChange} @@ -120,7 +120,6 @@ const SelectAdapter: React.FC = (props) => { placeholder={getTranslation(placeholder, i18n)} captureMenuScroll {...props} - customProps={selectProps} value={value} onChange={onChange} isDisabled={disabled} diff --git a/src/admin/components/elements/ReactSelect/types.ts b/src/admin/components/elements/ReactSelect/types.ts index d5aba05560..81b6e05e98 100644 --- a/src/admin/components/elements/ReactSelect/types.ts +++ b/src/admin/components/elements/ReactSelect/types.ts @@ -70,6 +70,10 @@ export type Props = { components?: { [key: string]: React.FC } + customProps?: CustomSelectProps + /** + * @deprecated Since version 1.0. Will be deleted in version 2.0. Use customProps instead. + */ selectProps?: CustomSelectProps backspaceRemovesValue?: boolean } diff --git a/src/admin/components/forms/field-types/Relationship/index.tsx b/src/admin/components/forms/field-types/Relationship/index.tsx index e80a009c20..a70f3b9b85 100644 --- a/src/admin/components/forms/field-types/Relationship/index.tsx +++ b/src/admin/components/forms/field-types/Relationship/index.tsx @@ -439,7 +439,7 @@ const Relationship: React.FC = (props) => { SingleValue, MultiValueLabel, }} - selectProps={{ + customProps={{ disableMouseDown: drawerIsOpen, disableKeyDown: drawerIsOpen, setDrawerIsOpen, diff --git a/src/admin/components/forms/field-types/Relationship/select-components/MultiValueLabel/index.tsx b/src/admin/components/forms/field-types/Relationship/select-components/MultiValueLabel/index.tsx index 27d3af21a7..5aadcd34fc 100644 --- a/src/admin/components/forms/field-types/Relationship/select-components/MultiValueLabel/index.tsx +++ b/src/admin/components/forms/field-types/Relationship/select-components/MultiValueLabel/index.tsx @@ -21,7 +21,7 @@ export const MultiValueLabel: React.FC> = (props) => { customProps: { setDrawerIsOpen, draggableProps, - onSave, + // onSave, } = {}, } = {}, } = props; @@ -70,7 +70,7 @@ export const MultiValueLabel: React.FC> = (props) => { - + )} diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index f3cc7f98ae..b4bea5af4a 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -911,7 +911,6 @@ describe('fields', () => { await page.locator('#action-save').click(); await expect(page.locator('.Toastify')).toContainText('successfully'); - // Create a new doc for the `relationshipHasMany` field await page.locator('#field-relationshipHasMany button.relationship-add-new__add-button').click(); const textField2 = page.locator('[id^=doc-drawer_text-fields_1_] #field-text'); @@ -924,12 +923,17 @@ describe('fields', () => { await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click(); // Now open the drawer again to edit the `text` field _using the keyboard_ + // Mimic real user behavior by typing into the field with spaces and backspaces + // Explicitly use both `down` and `type` to cover edge cases await page.locator('#field-relationshipHasMany button.relationship--multi-value-label__drawer-toggler').click(); - const textField3 = page.locator('[id^=doc-drawer_text-fields_1_] #field-text'); - await textField3.click(); + await page.locator('[id^=doc-drawer_text-fields_1_] #field-text').click(); await page.keyboard.down('1'); - await page.keyboard.down('2'); - await page.keyboard.down('3'); + await page.keyboard.type('23'); + await expect(await page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue(`${value}123`); + await page.keyboard.type('4567'); + await page.keyboard.press('Backspace'); + await expect(await page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue(`${value}123456`); + // save drawer await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click(); await expect(page.locator('.Toastify')).toContainText('successfully'); @@ -939,8 +943,9 @@ describe('fields', () => { await page.locator('#action-save').click(); await expect(page.locator('.Toastify')).toContainText('successfully'); await page.reload(); + // check if the value is saved - await expect(page.locator('#field-relationshipHasMany .relationship--multi-value-label__text')).toContainText(`${value}123`); + await expect(page.locator('#field-relationshipHasMany .relationship--multi-value-label__text')).toHaveText(`${value}123456`); }); }); From d112159d938d6731c23e7fb47d40a2e2e6af0563 Mon Sep 17 00:00:00 2001 From: Jarrod Flesch Date: Wed, 5 Jul 2023 15:26:10 -0400 Subject: [PATCH 10/12] docs: fix mdx formatting --- docs/admin/components.mdx | 6 ++---- docs/configuration/i18n.mdx | 6 ++---- docs/email/overview.mdx | 7 ++----- docs/fields/upload.mdx | 20 +++++--------------- docs/graphql/overview.mdx | 10 ++-------- docs/production/deployment.mdx | 28 ++++++---------------------- docs/queries/overview.mdx | 12 ++---------- 7 files changed, 21 insertions(+), 68 deletions(-) diff --git a/docs/admin/components.mdx b/docs/admin/components.mdx index e934dfe7b9..04864f0da6 100644 --- a/docs/admin/components.mdx +++ b/docs/admin/components.mdx @@ -11,10 +11,8 @@ While designing the Payload Admin panel, we determined it should be as minimal a To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly. - Tip: -
- Custom components will automatically be provided with all props that the - default component would accept. + Tip:
+ Custom components will automatically be provided with all props that the default component would accept.
### Base Component Overrides diff --git a/docs/configuration/i18n.mdx b/docs/configuration/i18n.mdx index 7cf562a7cb..2c39244d75 100644 --- a/docs/configuration/i18n.mdx +++ b/docs/configuration/i18n.mdx @@ -62,10 +62,8 @@ The Payload admin panel reads the language settings of a user's browser and disp After a user logs in, they can change their language selection in the `/account` view. - Note: -
- If there is a language that Payload does not yet support, we accept code - [contributions](https://github.com/payloadcms/payload/blob/master/contributing.md). + Note:
+ If there is a language that Payload does not yet support, we accept code [contributions](https://github.com/payloadcms/payload/blob/master/contributing.md).
### Node Express diff --git a/docs/email/overview.mdx b/docs/email/overview.mdx index d6bc406a88..74db975670 100644 --- a/docs/email/overview.mdx +++ b/docs/email/overview.mdx @@ -65,9 +65,7 @@ payload.init({ ``` - It is best practice to avoid saving credentials or API keys directly in your - code, use [environment - variables](/docs/configuration/overview#using-environment-variables-in-your-config). + It is best practice to avoid saving credentials or API keys directly in your code, use [environment variables](/docs/configuration/overview#using-environment-variables-in-your-config). ### Use an email service @@ -162,8 +160,7 @@ payload.init({ The mock email handler is used when payload is started with neither `transport` or `transportOptions` to know how to deliver email. - The randomly generated email account username and password will be different - each time the Payload server starts. + The randomly generated email account username and password will be different each time the Payload server starts. ### Using multiple mail providers diff --git a/docs/fields/upload.mdx b/docs/fields/upload.mdx index 65ba7025fd..ac2ad8304a 100644 --- a/docs/fields/upload.mdx +++ b/docs/fields/upload.mdx @@ -7,17 +7,12 @@ keywords: upload, images media, fields, config, configuration, documentation, Co --- - The Upload field allows for the selection of a Document from a collection - supporting Uploads, and formats the selection as a thumbnail in the Admin - panel. + The Upload field allows for the selection of a Document from a collection supporting Uploads, and formats the selection as a thumbnail in the Admin panel. - Important: -
- To use this field, you need to have a Collection configured to allow Uploads. - For more information, [click here](/docs/upload/overview) to read about how to - enable Uploads on a collection by collection basis. + Important:
+ To use this field, you need to have a Collection configured to allow Uploads. For more information, [click here](/docs/upload/overview) to read about how to enable Uploads on a collection by collection basis.
**Example uses:** @@ -100,11 +95,6 @@ const uploadField = { You can learn more about writing queries [here](/docs/queries/overview). - Note: -
- When an upload field has both filterOptions and a custom{" "} - validate function, the api will not validate{" "} - filterOptions unless you call the default upload field - validation function imported from payload/fields/validations{" "} - in your validate function. + Note:
+ When an upload field has both filterOptions and a custom validate function, the api will not validate filterOptions unless you call the default upload field validation function imported from payload/fields/validations in your validate function.
diff --git a/docs/graphql/overview.mdx b/docs/graphql/overview.mdx index 5d34b174bb..06f8aec819 100644 --- a/docs/graphql/overview.mdx +++ b/docs/graphql/overview.mdx @@ -115,14 +115,8 @@ GraphQL Playground is enabled by default for development purposes, but disabled You can even log in using the `login[collection-singular-label-here]` mutation to use the Playground as an authenticated user. - Tip: -
- To see more regarding how the above queries and mutations are used, visit your - GraphQL playground (by default at - [http://localhost:3000/api/graphql-playground](http://localhost:3000/api/graphql-playground)) - while your server is running. There, you can use the "Schema" and "Docs" - buttons on the right to see a ton of detail about how GraphQL operates within - Payload. + Tip:
+ To see more regarding how the above queries and mutations are used, visit your GraphQL playground (by default at [http://localhost:3000/api/graphql-playground](http://localhost:3000/api/graphql-playground)) while your server is running. There, you can use the "Schema" and "Docs" buttons on the right to see a ton of detail about how GraphQL operates within Payload.
## Query complexity limits diff --git a/docs/production/deployment.mdx b/docs/production/deployment.mdx index bd5ef5bb46..e7b5fab6bf 100644 --- a/docs/production/deployment.mdx +++ b/docs/production/deployment.mdx @@ -7,9 +7,7 @@ keywords: deployment, production, config, configuration, documentation, Content --- - So you've developed a Payload app, it's fully tested, and running great - locally. Now it's time to launch. Awesome! Great work! Now, - what's next? + So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to launch. Awesome! Great work! Now, what's next? There are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need to consider these main aspects: @@ -37,13 +35,7 @@ When you initialize Payload, you provide it with a `secret` property. This prope Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you wield that power responsibly before deploying to Production. - - By default, all Access Control functions require that a user is successfully - logged in to Payload to create, read, update, or delete data. - {" "} - But, if you allow public user registration, for example, you will want to make - sure that your access control functions are more strict - permitting{" "} - only appropriate users to perform appropriate actions. + By default, all Access Control functions require that a user is successfully logged in to Payload to create, read, update, or delete data. But, if you allow public user registration, for example, you will want to make sure that your access control functions are more strict - permitting only appropriate users to perform appropriate actions. ##### Building the Admin panel @@ -91,13 +83,8 @@ If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephem Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and backups. - Note: -
- If versions are enabled and a collection has many documents you may need a - minimum of an m10 mongoDB atlas cluster if you reach a sorting `exceeded - memory limit` error to view a collection list in the admin UI. The limitations - of the m2 and m5 tier clusters are here: [Atlas M0 (Free Cluster), M2, and M5 - Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations). + Note:
+ If versions are enabled and a collection has many documents you may need a minimum of an m10 mongoDB atlas cluster if you reach a sorting `exceeded memory limit` error to view a collection list in the admin UI. The limitations of the m2 and m5 tier clusters are here: [Atlas M0 (Free Cluster), M2, and M5 Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
##### DocumentDB @@ -131,11 +118,8 @@ Alternatively, persistent filesystems will never delete your files and can be tr - Many other more traditional web hosts - Warning: -
- If you rely on Payload's Upload functionality, make sure you - either use a host with a persistent filesystem or have an integration with a - third-party file host like Amazon S3. + Warning:
+ If you rely on Payload's Upload functionality, make sure you either use a host with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
##### Using ephemeral filesystem providers like Heroku diff --git a/docs/queries/overview.mdx b/docs/queries/overview.mdx index f42b970583..24d2c9bc05 100644 --- a/docs/queries/overview.mdx +++ b/docs/queries/overview.mdx @@ -9,13 +9,7 @@ keywords: query, documents, overview, documentation, Content Management System, Payload provides an extremely granular querying language through all APIs. Each API takes the same syntax and fully supports all options. - - Here, "querying" relates to filtering or searching through documents within - a Collection. - {" "} - You can build queries to pass to Find operations as well as to [restrict which - documents certain users can access](/docs/access-control/overview) via access - control functions. + Here, "querying" relates to filtering or searching through documents within a Collection. You can build queries to pass to Find operations as well as to [restrict which documents certain users can access](/docs/access-control/overview) via access control functions. ### Simple queries @@ -74,9 +68,7 @@ The above example demonstrates a simple query but you can get much more complex. Tip:
- If you know your users will be querying on certain fields a lot, you can add - index: true - to a field's config which will speed up searches using that field immensely. + If you know your users will be querying on certain fields a lot, you can add index: true to a field's config which will speed up searches using that field immensely.
### And / Or Logic From 1bf81f56ce10293e16626e6a040b939d61117152 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Wed, 5 Jul 2023 16:15:29 -0400 Subject: [PATCH 11/12] chore(release): v1.11.0 --- CHANGELOG.md | 17 +++++++++++++++++ package.json | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1f2a68c1..b979e4356f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ +# [1.11.0](https://github.com/payloadcms/payload/compare/v1.10.5...v1.11.0) (2023-07-05) + + +### Bug Fixes + +* ensures fields within blocks respect field level access control ([#2969](https://github.com/payloadcms/payload/issues/2969)) ([5b79067](https://github.com/payloadcms/payload/commit/5b79067cc14874abbd1e1a5b6e619d41571b187f)) +* ensures rows always have id's ([#2968](https://github.com/payloadcms/payload/issues/2968)) ([04851d0](https://github.com/payloadcms/payload/commit/04851d0dc99e4a3df0a1ac642e9a4b9a3c06d8a1)) +* GraphQL type for number field ([#2954](https://github.com/payloadcms/payload/issues/2954)) ([29d8bf0](https://github.com/payloadcms/payload/commit/29d8bf0927038d2305218e5a6b811e0c4039d617)) +* nested richtext bug and test ([#2966](https://github.com/payloadcms/payload/issues/2966)) ([801f609](https://github.com/payloadcms/payload/commit/801f60939b1bb4e33fbabe1f9a3c4a04a47912db)) +* properly threads custom react-select props through relationship field ([#2973](https://github.com/payloadcms/payload/issues/2973)) ([79393e8](https://github.com/payloadcms/payload/commit/79393e8cf0b79b31fa711536e0bc22b1a251468a)) + + +### Features + +* improve typing of ExtendableError and APIError ([#2864](https://github.com/payloadcms/payload/issues/2864)) ([7c47e4b](https://github.com/payloadcms/payload/commit/7c47e4b0d3c63f6f7800daaf424935d6067ffcc4)) +* narrow endpoint.method type ([#1880](https://github.com/payloadcms/payload/issues/1880)) ([b734a1c](https://github.com/payloadcms/payload/commit/b734a1c422d200cad1085b7e92f8540df4238e32)) + ## [1.10.5](https://github.com/payloadcms/payload/compare/v1.10.4...v1.10.5) (2023-06-30) diff --git a/package.json b/package.json index 49c0b1c76b..9944663c85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "payload", - "version": "1.10.5", + "version": "1.11.0", "description": "Node, React and MongoDB Headless CMS and Application Framework", "license": "MIT", "engines": { From 1877d2247c89ca5c8e1f0e1f989154d54768fed8 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Wed, 5 Jul 2023 16:20:25 -0400 Subject: [PATCH 12/12] fix: anchor Button component respect margins (#2648) --- src/admin/components/elements/Button/index.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/admin/components/elements/Button/index.scss b/src/admin/components/elements/Button/index.scss index 5662fb3a27..aa5e64e099 100644 --- a/src/admin/components/elements/Button/index.scss +++ b/src/admin/components/elements/Button/index.scss @@ -1,5 +1,9 @@ @import '../../../scss/styles.scss'; +a.btn { + display: inline-block; +} + .btn { background: transparent; line-height: base(1);