diff --git a/config/forms.php b/config/forms.php index 37f94e2d256..2190be43184 100644 --- a/config/forms.php +++ b/config/forms.php @@ -47,6 +47,19 @@ 'send_email_job' => \Statamic\Forms\SendEmail::class, + /* + |-------------------------------------------------------------------------- + | Connections Cache Store + |-------------------------------------------------------------------------- + | + | The cache store used to track when a submission's connections have all + | finished, so its temporary files can be deleted. A persistent store + | such as "database" or "redis" is recommended. Null uses the default. + | + */ + + 'connections_cache_store' => null, + /* |-------------------------------------------------------------------------- | Partial Submissions diff --git a/lang/en/messages.php b/lang/en/messages.php index 2892614df49..d6a18684815 100644 --- a/lang/en/messages.php +++ b/lang/en/messages.php @@ -83,6 +83,12 @@ 'elevated_session_verification_code_notification_body' => 'Copy and paste the code shown below to verify your identity. If you didn\'t request this code, you can ignore this email.', 'elevated_session_verification_code_notification_subject' => 'Your Verification Code', 'elevated_session_verification_code_sent' => 'Verification code has been sent to your email.', + 'email_connection_delete_confirmation' => 'Are you sure you want to delete this email?', + 'email_connection_instructions' => 'Set up emails to be sent automatically when this form is submitted. Use them to notify users or team members.', + 'email_connection_preview_attachments' => 'Files uploaded with the submission will be attached.', + 'email_connection_preview_latest_instructions' => 'Rendered with the latest submission to this form.', + 'email_connection_preview_sample_instructions' => 'Rendered with sample submission data. Real emails will contain the submitted values.', + 'email_connector_description' => 'Send email notifications.', 'email_utility_configuration_description' => 'Mail settings are configured in :path', 'email_utility_description' => 'Check email configuration settings and send test emails.', 'entry_count' => ':count entry|:count entries', @@ -123,22 +129,18 @@ 'forgot_password_enter_email' => 'Enter your email address to receive a password reset link.', 'forgot_password_sent' => 'If an account with that email address exists, a password reset link has been sent.', 'form_configure_blueprint_instructions' => 'Choose from existing blueprints or create a new one.', - 'form_configure_email_attachments_instructions' => 'Attach uploaded assets to this email.', - 'form_configure_email_bcc_instructions' => 'Email address of the BCC recipient(s) - comma separated.', - 'form_configure_email_cc_instructions' => 'Email address of the CC recipient(s) - comma separated.', + 'form_configure_email_attachments_instructions' => 'Attach files uploaded with the submission.', + 'form_configure_email_body_instructions' => 'You can include submitted values using [Antlers](https://statamic.dev/frontend/antlers). Leave blank to list all submitted fields instead.', 'form_configure_email_from_instructions' => 'Leave blank to fall back to the site default.', - 'form_configure_email_html_instructions' => 'The view for the HTML version of this email.', - 'form_configure_email_instructions' => 'Configure emails to be sent when new form submissions are received.', + 'form_configure_email_views_instructions' => 'Leave both blank to use the default layout.', 'form_configure_email_description' => 'Be sure your email settings are configured to send and receive notifications when forms are submitted.', 'form_configure_email_markdown_instructions' => 'Render the HTML version of this email using Markdown.', - 'form_configure_email_reply_to_instructions' => 'Leave blank to fall back to sender.', - 'form_configure_email_subject_instructions' => 'Email subject line.', - 'form_configure_email_text_instructions' => 'The view for the text version of this email.', - 'form_configure_email_to_instructions' => 'Email address of the recipient(s) - comma separated.', + 'form_configure_email_reply_to_instructions' => 'Leave blank to fall back to the sender.', + 'form_configure_email_to_instructions' => 'Choose a form field or enter an email address.', 'form_configure_handle_instructions' => 'Used to reference this form on the frontend. This cannot be easily changed later.', 'form_configure_honeypot_instructions' => 'Field name to use as a honeypot. Honeypots are special fields used to reduce bot spam.', 'form_configure_intro' => 'Forms collect information from visitors and can trigger events and send notifications when submissions are received.', - 'form_configure_mailer_instructions' => 'Choose the mailer for sending this email. Leave blank to fall back to the default mailer.', + 'form_configure_mailer_instructions' => 'Leave blank to fall back to the default mailer.', 'form_configure_generate_fake_submissions_instructions' => 'Allow generating fake submissions and workflow testing from the submissions screen.', 'form_configure_store_instructions' => 'Disable to stop storing submissions. Events and email notifications will still be sent.', 'form_configure_title_instructions' => 'Use a call to action, such as \'Contact Us\'.', @@ -331,5 +333,10 @@ 'user_wizard_invitation_subject' => 'Activate your new Statamic account on :site', 'user_wizard_roles_groups_intro' => 'Users can be assigned to roles that customize their permissions, access, and abilities throughout the Control Panel.', 'user_wizard_super_admin_instructions' => 'Super admins have complete control and access to everything in the control panel. Grant this role wisely.', + 'webhook_connection_delete_confirmation' => 'Are you sure you want to delete this webhook?', + 'webhook_connection_instructions' => 'Each webhook is sent when this form is submitted. Notify other services or trigger automations with the submission data.', + 'webhook_connection_payload_instructions' => 'Each submission is sent to the webhook URL as a JSON POST request.', + 'webhook_connection_verify_ssl_instructions' => 'Only disable this if the receiving server uses a self-signed certificate.', + 'webhook_connector_description' => 'Send submissions to an external URL.', 'width_x_height' => ':width × :height', ]; diff --git a/lang/en/validation.php b/lang/en/validation.php index be3b1bd615e..c2d7b9d5279 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -196,6 +196,7 @@ 'elevated_session_resend_code_unavailable' => 'Resend code is only available for verification code method.', 'elevated_session_verification_code' => 'The verification code is incorrect.', 'email_available' => 'A user with this email already exists.', + 'email_field_reference' => "References a field that doesn't exist on this form.", 'fieldset_imported_recursively' => 'Fieldset :handle is being imported recursively.', 'form_chart_hidden_field' => 'Field :field is hidden and cannot be charted.', 'form_chart_not_applicable' => 'Chart :chart cannot be used for field :field.', @@ -218,6 +219,7 @@ 'unique_user_value' => 'This value has already been taken.', 'unique_uri' => 'This URI has already been taken.', 'time' => 'Not a valid time.', + 'webhook_url_not_public' => 'Webhooks must use a public address.', 'asset_current_filename' => 'This is the current filename.', 'asset_file_exists' => 'A file already exists with this name.', 'asset_file_exists_same_content' => 'A file already exists with this name and has the same content. You may want to delete this rather than rename it.', diff --git a/packages/cms/src/index.js b/packages/cms/src/index.js index 8b30e9f288b..cbee0e5568c 100644 --- a/packages/cms/src/index.js +++ b/packages/cms/src/index.js @@ -16,6 +16,10 @@ export const { RevisionPreview, SaveButtonOptions, SortableList, + ConnectionFields, + ConnectionList, + ConnectionRules, + ConnectionSummary, requireElevatedSession, requireElevatedSessionIf, clone, diff --git a/resources/css/dark.css b/resources/css/dark.css index 64ee69737ff..ef9a2844b2d 100644 --- a/resources/css/dark.css +++ b/resources/css/dark.css @@ -1,6 +1,8 @@ .dark { + color-scheme: dark; + input:-webkit-autofill { -webkit-box-shadow: inset 0 0 0 50px var(--color-gray-500); -webkit-text-fill-color: var(--color-gray-300); diff --git a/resources/css/elements/base.css b/resources/css/elements/base.css index 236115cc450..3b219d3e6b7 100644 --- a/resources/css/elements/base.css +++ b/resources/css/elements/base.css @@ -1,6 +1,8 @@ /* VARIABLES =================================================== */ :root { + color-scheme: light; + /* For animating height */ interpolate-size: allow-keywords; diff --git a/resources/js/bootstrap/cms/core.js b/resources/js/bootstrap/cms/core.js index 05070c7d6c8..505283c7260 100644 --- a/resources/js/bootstrap/cms/core.js +++ b/resources/js/bootstrap/cms/core.js @@ -19,3 +19,7 @@ export { default as resetValuesFromResponse } from '../../util/resetValuesFromRe export { requireElevatedSession, requireElevatedSessionIf } from '../../components/elevated-sessions'; export { default as clone, deepClone } from '../../util/clone.js'; export { default as debounce } from '../../util/debounce.js'; +export { default as ConnectionFields } from '../../components/forms/connections/ConnectionFields.vue'; +export { default as ConnectionList } from '../../components/forms/connections/ConnectionList.vue'; +export { default as ConnectionRules } from '../../components/forms/connections/ConnectionRules.vue'; +export { default as ConnectionSummary } from '../../components/forms/connections/ConnectionSummary.vue'; diff --git a/resources/js/bootstrap/components.js b/resources/js/bootstrap/components.js index fb7fdabc901..4e715e66b49 100644 --- a/resources/js/bootstrap/components.js +++ b/resources/js/bootstrap/components.js @@ -70,6 +70,10 @@ export default function registerGlobalComponents(app) { app.component('min-max-insight', MinMaxInsight); app.component('star-rating-insight', StarRatingInsight); + // Form Connections + app.component('email-connector', defineAsyncComponent(() => import('../components/forms/connections/EmailConnector.vue'))); + app.component('webhook-connector', defineAsyncComponent(() => import('../components/forms/connections/WebhookConnector.vue'))); + // Reusable app.component('file-icon', FileIcon); diff --git a/resources/js/bootstrap/fieldtypes.js b/resources/js/bootstrap/fieldtypes.js index 1b9b078f200..6f34b4f4691 100644 --- a/resources/js/bootstrap/fieldtypes.js +++ b/resources/js/bootstrap/fieldtypes.js @@ -31,6 +31,7 @@ import Sites from '../components/globals/Sites.vue'; import Grid from '../components/fieldtypes/grid/Grid.vue'; import GridIndex from '../components/fieldtypes/grid/GridIndex.vue'; import GroupFieldtype from '../components/fieldtypes/GroupFieldtype.vue'; +import FormFieldsFieldtype from '../components/fieldtypes/FormFieldsFieldtype.vue'; import FormHeadingFieldtype from '../components/fieldtypes/FormHeadingFieldtype.vue'; import FormParagraphFieldtype from '@/components/fieldtypes/FormParagraphFieldtype.vue'; import FormUploadFieldtype from '@/components/fieldtypes/FormUploadFieldtype.vue'; @@ -114,6 +115,7 @@ export default function registerFieldtypes(app) { app.component('grid-fieldtype', Grid); app.component('grid-fieldtype-index', GridIndex); app.component('group-fieldtype', GroupFieldtype); + app.component('form_fields-fieldtype', FormFieldsFieldtype); app.component('form_heading-fieldtype', FormHeadingFieldtype); app.component('form_paragraph-fieldtype', FormParagraphFieldtype); app.component('form_upload-fieldtype', FormUploadFieldtype); diff --git a/resources/js/components/fieldtypes/FormFieldsFieldtype.vue b/resources/js/components/fieldtypes/FormFieldsFieldtype.vue new file mode 100644 index 00000000000..83a3a970d68 --- /dev/null +++ b/resources/js/components/fieldtypes/FormFieldsFieldtype.vue @@ -0,0 +1,51 @@ + + + diff --git a/resources/js/components/forms/connections/ConnectionFields.vue b/resources/js/components/forms/connections/ConnectionFields.vue new file mode 100644 index 00000000000..b3bb8fde19b --- /dev/null +++ b/resources/js/components/forms/connections/ConnectionFields.vue @@ -0,0 +1,78 @@ + + + diff --git a/resources/js/components/forms/connections/ConnectionItem.vue b/resources/js/components/forms/connections/ConnectionItem.vue new file mode 100644 index 00000000000..22bd2a3b9f9 --- /dev/null +++ b/resources/js/components/forms/connections/ConnectionItem.vue @@ -0,0 +1,73 @@ + + + diff --git a/resources/js/components/forms/connections/ConnectionList.vue b/resources/js/components/forms/connections/ConnectionList.vue new file mode 100644 index 00000000000..4ad65181b49 --- /dev/null +++ b/resources/js/components/forms/connections/ConnectionList.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/resources/js/components/forms/connections/ConnectionRules.vue b/resources/js/components/forms/connections/ConnectionRules.vue new file mode 100644 index 00000000000..ed0975858b4 --- /dev/null +++ b/resources/js/components/forms/connections/ConnectionRules.vue @@ -0,0 +1,246 @@ + + + + + diff --git a/resources/js/components/forms/connections/ConnectionSummary.vue b/resources/js/components/forms/connections/ConnectionSummary.vue new file mode 100644 index 00000000000..04cd90e5143 --- /dev/null +++ b/resources/js/components/forms/connections/ConnectionSummary.vue @@ -0,0 +1,124 @@ + + + diff --git a/resources/js/components/forms/connections/EmailConnector.vue b/resources/js/components/forms/connections/EmailConnector.vue new file mode 100644 index 00000000000..90702899ec2 --- /dev/null +++ b/resources/js/components/forms/connections/EmailConnector.vue @@ -0,0 +1,74 @@ + + + diff --git a/resources/js/components/forms/connections/EmailPreview.vue b/resources/js/components/forms/connections/EmailPreview.vue new file mode 100644 index 00000000000..24d4906682b --- /dev/null +++ b/resources/js/components/forms/connections/EmailPreview.vue @@ -0,0 +1,100 @@ + + +