Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/changelog-1.9.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ All changes included in 1.9:
### `email`

- ([#13882](https://github.com/quarto-dev/quarto-cli/pull/13882)): Add support for multiple email outputs when rendering to `format: email` for Posit Connect.
- ([#14021](https://github.com/quarto-dev/quarto-cli/issues/14021)): Add `email-version` hook to override detected Connect version when rendering emails for Posit Connect.

### `html`

Expand Down
38 changes: 31 additions & 7 deletions src/resources/editor/tools/vs-code.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23370,6 +23370,11 @@ var require_yaml_intelligence_resources = __commonJS({
"Write markdown links as references rather than inline.",
"Unique prefix for references (<code>none</code> to prevent automatic\nprefixes)",
"Automatically re-render for preview whenever document is saved (note\nthat this requires a preview for the saved document be already running).\nThis option currently works only within VS Code.",
{
short: "Editor-specific options (used by RStudio and Positron).",
long: "Editor-specific options that control IDE behavior for this document.\nThese options are used by RStudio and Positron to configure per-document\neditor settings."
},
"Determines where chunk output is shown in the editor.",
"Enable (<code>true</code>) or disable (<code>false</code>) Zotero for\na document. Alternatively, provide a list of one or more Zotero group\nlibraries to use with the document.",
"The identifier for this publication.",
"The identifier value.",
Expand Down Expand Up @@ -25097,10 +25102,9 @@ var require_yaml_intelligence_resources = __commonJS({
"internal-schema-hack",
"List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto\u2019s default order\nis \u2018knitr\u2019, \u2018jupyter\u2019, \u2018markdown\u2019, \u2018julia\u2019.",
{
short: "Editor-specific options (used by RStudio and Positron).",
long: "Editor-specific options that control IDE behavior for this document.\nThese options are used by RStudio and Positron to configure per-document\neditor settings."
},
"Determines where chunk output is shown in the editor."
short: "Email format version",
long: "Specifies which email format version to use."
}
],
"schema/external-schemas.yml": [
{
Expand Down Expand Up @@ -25330,12 +25334,12 @@ var require_yaml_intelligence_resources = __commonJS({
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
_internalId: 221795,
_internalId: 222358,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
_internalId: 221787,
_internalId: 222350,
type: "enum",
enum: [
"png",
Expand All @@ -25351,7 +25355,7 @@ var require_yaml_intelligence_resources = __commonJS({
exhaustiveCompletions: true
},
theme: {
_internalId: 221794,
_internalId: 222357,
type: "anyOf",
anyOf: [
{
Expand Down Expand Up @@ -25488,6 +25492,26 @@ var require_yaml_intelligence_resources = __commonJS({
long: "Controls how theorems, lemmas, definitions, etc. are rendered:\n- `simple`: Plain text with bold title and italic body (default)\n- `fancy`: Colored boxes using brand colors\n- `clouds`: Rounded colored background boxes\n- `rainbow`: Colored left border with colored title\n"
}
}
],
"schema/document-email.yml": [
{
name: "email-version",
tags: {
formats: [
"email"
]
},
schema: {
enum: [
1,
2
]
},
description: {
short: "Email format version",
long: "Specifies which email format version to use.\n\n- `1`: Legacy email format with document-level metadata (compatible with older Connect versions)\n- `2`: New email format with multiple individual emails and v2 markers (requires Posit Connect 2026.03 or later)\n"
}
}
]
};
}
Expand Down

Large diffs are not rendered by default.

38 changes: 31 additions & 7 deletions src/resources/editor/tools/yaml/web-worker.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 31 additions & 7 deletions src/resources/editor/tools/yaml/yaml-intelligence-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -16342,6 +16342,11 @@
"Write markdown links as references rather than inline.",
"Unique prefix for references (<code>none</code> to prevent automatic\nprefixes)",
"Automatically re-render for preview whenever document is saved (note\nthat this requires a preview for the saved document be already running).\nThis option currently works only within VS Code.",
{
"short": "Editor-specific options (used by RStudio and Positron).",
"long": "Editor-specific options that control IDE behavior for this document.\nThese options are used by RStudio and Positron to configure per-document\neditor settings."
},
"Determines where chunk output is shown in the editor.",
"Enable (<code>true</code>) or disable (<code>false</code>) Zotero for\na document. Alternatively, provide a list of one or more Zotero group\nlibraries to use with the document.",
"The identifier for this publication.",
"The identifier value.",
Expand Down Expand Up @@ -18069,10 +18074,9 @@
"internal-schema-hack",
"List execution engines you want to give priority when determining\nwhich engine should render a notebook. If two engines have support for a\nnotebook, the one listed earlier will be chosen. Quarto’s default order\nis ‘knitr’, ‘jupyter’, ‘markdown’, ‘julia’.",
{
"short": "Editor-specific options (used by RStudio and Positron).",
"long": "Editor-specific options that control IDE behavior for this document.\nThese options are used by RStudio and Positron to configure per-document\neditor settings."
},
"Determines where chunk output is shown in the editor."
"short": "Email format version",
"long": "Specifies which email format version to use."
}
],
"schema/external-schemas.yml": [
{
Expand Down Expand Up @@ -18302,12 +18306,12 @@
"mermaid": "%%"
},
"handlers/mermaid/schema.yml": {
"_internalId": 221795,
"_internalId": 222358,
"type": "object",
"description": "be an object",
"properties": {
"mermaid-format": {
"_internalId": 221787,
"_internalId": 222350,
"type": "enum",
"enum": [
"png",
Expand All @@ -18323,7 +18327,7 @@
"exhaustiveCompletions": true
},
"theme": {
"_internalId": 221794,
"_internalId": 222357,
"type": "anyOf",
"anyOf": [
{
Expand Down Expand Up @@ -18460,5 +18464,25 @@
"long": "Controls how theorems, lemmas, definitions, etc. are rendered:\n- `simple`: Plain text with bold title and italic body (default)\n- `fancy`: Colored boxes using brand colors\n- `clouds`: Rounded colored background boxes\n- `rainbow`: Colored left border with colored title\n"
}
}
],
"schema/document-email.yml": [
{
"name": "email-version",
"tags": {
"formats": [
"email"
]
},
"schema": {
"enum": [
1,
2
]
},
"description": {
"short": "Email format version",
"long": "Specifies which email format version to use.\n\n- `1`: Legacy email format with document-level metadata (compatible with older Connect versions)\n- `2`: New email format with multiple individual emails and v2 markers (requires Posit Connect 2026.03 or later)\n"
}
}
]
}
17 changes: 16 additions & 1 deletion src/resources/filters/modules/connectversion.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ Connect version sniffing utilities for email extension
Functions to detect and compare Posit Connect versions from environment variables
]]

-- Get email format version override from metadata
-- Can be set in YAML as:
-- format:
-- email:
-- email-version: 2
-- Returns: version string (e.g., "2") if override is set, nil otherwise
function get_email_format_override(meta)
if meta and meta["email-version"] then
return pandoc.utils.stringify(meta["email-version"])
end

return nil
end

-- Parse Connect version from SPARK_CONNECT_USER_AGENT
-- Format: posit-connect/2024.09.0
-- posit-connect/2024.09.0-dev+26-dirty-g51b853f70e
Expand Down Expand Up @@ -70,5 +84,6 @@ end

-- Export functions for module usage
return {
is_connect_version_at_least = is_connect_version_at_least
is_connect_version_at_least = is_connect_version_at_least,
get_email_format_override = get_email_format_override
}
20 changes: 16 additions & 4 deletions src/resources/filters/quarto-post/email.lua
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,22 @@ function process_document(doc)
return doc
end

-- Detect format upfront: Check Connect version and look for document-level metadata
-- This must be determined before processing to avoid confusion about which format to use
if connectversion.is_connect_version_at_least(constants.kConnectEmailMetadataChangeVersion) then
connect_supports_v2 = true
-- Check for explicit email format override first
local format_override = connectversion.get_email_format_override(doc.meta)
if format_override then
-- If override is set, interpret the value as a string
if format_override == "2" then
connect_supports_v2 = true
io.stderr:write("WARNING: Email format v2 is being forced via 'format.email.version: " .. format_override .. "' in YAML. This overrides the Connect version detection.\n")
else
connect_supports_v2 = false
io.stderr:write("WARNING: Email format v1 is being forced via 'format.email.version: " .. format_override .. "' in YAML. This overrides the Connect version detection.\n")
end
else
-- Fall back to version sniffing if no explicit override
if connectversion.is_connect_version_at_least(constants.kConnectEmailMetadataChangeVersion) then
connect_supports_v2 = true
end
end

-- Scan for document-level metadata at the TOP LEVEL of the document
Expand Down
12 changes: 12 additions & 0 deletions src/resources/schema/document-email.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- name: email-version
tags:
formats: [email]
schema:
enum: [1, 2]
description:
short: "Email format version"
long: |
Specifies which email format version to use.

- `1`: Legacy email format with document-level metadata (compatible with older Connect versions)
- `2`: New email format with multiple individual emails and v2 markers (requires Posit Connect 2026.03 or later)
28 changes: 28 additions & 0 deletions tests/docs/email/email-force-v2.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Email Test Document
author: Charles Teague
format:
email:
email-version: 2
---

The report content. Anything that is here is not part of the email message.

::: {.email}

::: {.subject}
The subject line.
:::

::: {.email-text}
An optional text-only version of the email message..
:::

The HTML email content. Here you can add code cells, produce images, and write accompanying text.
This content is not seen when viewing the rendered document on Connect (it's only
seen when sending an email from the Connect document page). Emails from Connect
can be sent manually, and they can also be scheduled.

:::

Any additional report content not part of the email message.
17 changes: 17 additions & 0 deletions tests/smoke/render/render-email.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,23 @@ testRender(docs("email/email-multi-v2.qmd"), "email", false, [
}
});

// Test v2 format override with old Connect version
// Uses email-format: v2 in YAML to force v2 despite old Connect version
testRender(docs("email/email-force-v2.qmd"), "email", false, [fileExists(previewFileV2_1), validJsonWithMultipleEmails(jsonFile, 1, {
"0": {
"email_id": 1,
"subject": "The subject line.",
"attachments": [],
"suppress_scheduled": false,
"send_report_as_attachment": false
}
})], {
...cleanupCtx,
env: {
"SPARK_CONNECT_USER_AGENT": "posit-connect/2024.09.0"
}
});

// Test mixed metadata - some emails have metadata, others don't
testRender(docs("email/email-mixed-metadata-v2.qmd"), "email", false, [
fileExists(previewFileV2_1),
Expand Down
Loading