Skip to content

Commit 76b9460

Browse files
authored
Merge pull request #14022 from juleswg23/feat-connect-version-override
add `email-version` hook to override detected Connect version
1 parent 148d142 commit 76b9460

10 files changed

Lines changed: 184 additions & 27 deletions

File tree

news/changelog-1.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ All changes included in 1.9:
3434
### `email`
3535

3636
- ([#13882](https://github.com/quarto-dev/quarto-cli/pull/13882)): Add support for multiple email outputs when rendering to `format: email` for Posit Connect.
37+
- ([#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.
3738

3839
### `html`
3940

src/resources/editor/tools/vs-code.mjs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23370,6 +23370,11 @@ var require_yaml_intelligence_resources = __commonJS({
2337023370
"Write markdown links as references rather than inline.",
2337123371
"Unique prefix for references (<code>none</code> to prevent automatic\nprefixes)",
2337223372
"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.",
23373+
{
23374+
short: "Editor-specific options (used by RStudio and Positron).",
23375+
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."
23376+
},
23377+
"Determines where chunk output is shown in the editor.",
2337323378
"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.",
2337423379
"The identifier for this publication.",
2337523380
"The identifier value.",
@@ -25097,10 +25102,9 @@ var require_yaml_intelligence_resources = __commonJS({
2509725102
"internal-schema-hack",
2509825103
"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.",
2509925104
{
25100-
short: "Editor-specific options (used by RStudio and Positron).",
25101-
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."
25102-
},
25103-
"Determines where chunk output is shown in the editor."
25105+
short: "Email format version",
25106+
long: "Specifies which email format version to use."
25107+
}
2510425108
],
2510525109
"schema/external-schemas.yml": [
2510625110
{
@@ -25330,12 +25334,12 @@ var require_yaml_intelligence_resources = __commonJS({
2533025334
mermaid: "%%"
2533125335
},
2533225336
"handlers/mermaid/schema.yml": {
25333-
_internalId: 221795,
25337+
_internalId: 222358,
2533425338
type: "object",
2533525339
description: "be an object",
2533625340
properties: {
2533725341
"mermaid-format": {
25338-
_internalId: 221787,
25342+
_internalId: 222350,
2533925343
type: "enum",
2534025344
enum: [
2534125345
"png",
@@ -25351,7 +25355,7 @@ var require_yaml_intelligence_resources = __commonJS({
2535125355
exhaustiveCompletions: true
2535225356
},
2535325357
theme: {
25354-
_internalId: 221794,
25358+
_internalId: 222357,
2535525359
type: "anyOf",
2535625360
anyOf: [
2535725361
{
@@ -25488,6 +25492,26 @@ var require_yaml_intelligence_resources = __commonJS({
2548825492
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"
2548925493
}
2549025494
}
25495+
],
25496+
"schema/document-email.yml": [
25497+
{
25498+
name: "email-version",
25499+
tags: {
25500+
formats: [
25501+
"email"
25502+
]
25503+
},
25504+
schema: {
25505+
enum: [
25506+
1,
25507+
2
25508+
]
25509+
},
25510+
description: {
25511+
short: "Email format version",
25512+
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"
25513+
}
25514+
}
2549125515
]
2549225516
};
2549325517
}

src/resources/editor/tools/yaml/all-schema-definitions.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/resources/editor/tools/yaml/web-worker.js

Lines changed: 31 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/resources/editor/tools/yaml/yaml-intelligence-resources.json

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16342,6 +16342,11 @@
1634216342
"Write markdown links as references rather than inline.",
1634316343
"Unique prefix for references (<code>none</code> to prevent automatic\nprefixes)",
1634416344
"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.",
16345+
{
16346+
"short": "Editor-specific options (used by RStudio and Positron).",
16347+
"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."
16348+
},
16349+
"Determines where chunk output is shown in the editor.",
1634516350
"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.",
1634616351
"The identifier for this publication.",
1634716352
"The identifier value.",
@@ -18069,10 +18074,9 @@
1806918074
"internal-schema-hack",
1807018075
"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’.",
1807118076
{
18072-
"short": "Editor-specific options (used by RStudio and Positron).",
18073-
"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."
18074-
},
18075-
"Determines where chunk output is shown in the editor."
18077+
"short": "Email format version",
18078+
"long": "Specifies which email format version to use."
18079+
}
1807618080
],
1807718081
"schema/external-schemas.yml": [
1807818082
{
@@ -18302,12 +18306,12 @@
1830218306
"mermaid": "%%"
1830318307
},
1830418308
"handlers/mermaid/schema.yml": {
18305-
"_internalId": 221795,
18309+
"_internalId": 222358,
1830618310
"type": "object",
1830718311
"description": "be an object",
1830818312
"properties": {
1830918313
"mermaid-format": {
18310-
"_internalId": 221787,
18314+
"_internalId": 222350,
1831118315
"type": "enum",
1831218316
"enum": [
1831318317
"png",
@@ -18323,7 +18327,7 @@
1832318327
"exhaustiveCompletions": true
1832418328
},
1832518329
"theme": {
18326-
"_internalId": 221794,
18330+
"_internalId": 222357,
1832718331
"type": "anyOf",
1832818332
"anyOf": [
1832918333
{
@@ -18460,5 +18464,25 @@
1846018464
"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"
1846118465
}
1846218466
}
18467+
],
18468+
"schema/document-email.yml": [
18469+
{
18470+
"name": "email-version",
18471+
"tags": {
18472+
"formats": [
18473+
"email"
18474+
]
18475+
},
18476+
"schema": {
18477+
"enum": [
18478+
1,
18479+
2
18480+
]
18481+
},
18482+
"description": {
18483+
"short": "Email format version",
18484+
"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"
18485+
}
18486+
}
1846318487
]
1846418488
}

src/resources/filters/modules/connectversion.lua

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ Connect version sniffing utilities for email extension
44
Functions to detect and compare Posit Connect versions from environment variables
55
]]
66

7+
-- Get email format version override from metadata
8+
-- Can be set in YAML as:
9+
-- format:
10+
-- email:
11+
-- email-version: 2
12+
-- Returns: version string (e.g., "2") if override is set, nil otherwise
13+
function get_email_format_override(meta)
14+
if meta and meta["email-version"] then
15+
return pandoc.utils.stringify(meta["email-version"])
16+
end
17+
18+
return nil
19+
end
20+
721
-- Parse Connect version from SPARK_CONNECT_USER_AGENT
822
-- Format: posit-connect/2024.09.0
923
-- posit-connect/2024.09.0-dev+26-dirty-g51b853f70e
@@ -70,5 +84,6 @@ end
7084

7185
-- Export functions for module usage
7286
return {
73-
is_connect_version_at_least = is_connect_version_at_least
87+
is_connect_version_at_least = is_connect_version_at_least,
88+
get_email_format_override = get_email_format_override
7489
}

src/resources/filters/quarto-post/email.lua

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,10 +347,22 @@ function process_document(doc)
347347
return doc
348348
end
349349

350-
-- Detect format upfront: Check Connect version and look for document-level metadata
351-
-- This must be determined before processing to avoid confusion about which format to use
352-
if connectversion.is_connect_version_at_least(constants.kConnectEmailMetadataChangeVersion) then
353-
connect_supports_v2 = true
350+
-- Check for explicit email format override first
351+
local format_override = connectversion.get_email_format_override(doc.meta)
352+
if format_override then
353+
-- If override is set, interpret the value as a string
354+
if format_override == "2" then
355+
connect_supports_v2 = true
356+
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")
357+
else
358+
connect_supports_v2 = false
359+
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")
360+
end
361+
else
362+
-- Fall back to version sniffing if no explicit override
363+
if connectversion.is_connect_version_at_least(constants.kConnectEmailMetadataChangeVersion) then
364+
connect_supports_v2 = true
365+
end
354366
end
355367

356368
-- Scan for document-level metadata at the TOP LEVEL of the document
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
- name: email-version
2+
tags:
3+
formats: [email]
4+
schema:
5+
enum: [1, 2]
6+
description:
7+
short: "Email format version"
8+
long: |
9+
Specifies which email format version to use.
10+
11+
- `1`: Legacy email format with document-level metadata (compatible with older Connect versions)
12+
- `2`: New email format with multiple individual emails and v2 markers (requires Posit Connect 2026.03 or later)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: Email Test Document
3+
author: Charles Teague
4+
format:
5+
email:
6+
email-version: 2
7+
---
8+
9+
The report content. Anything that is here is not part of the email message.
10+
11+
::: {.email}
12+
13+
::: {.subject}
14+
The subject line.
15+
:::
16+
17+
::: {.email-text}
18+
An optional text-only version of the email message..
19+
:::
20+
21+
The HTML email content. Here you can add code cells, produce images, and write accompanying text.
22+
This content is not seen when viewing the rendered document on Connect (it's only
23+
seen when sending an email from the Connect document page). Emails from Connect
24+
can be sent manually, and they can also be scheduled.
25+
26+
:::
27+
28+
Any additional report content not part of the email message.

tests/smoke/render/render-email.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,23 @@ testRender(docs("email/email-multi-v2.qmd"), "email", false, [
183183
}
184184
});
185185

186+
// Test v2 format override with old Connect version
187+
// Uses email-format: v2 in YAML to force v2 despite old Connect version
188+
testRender(docs("email/email-force-v2.qmd"), "email", false, [fileExists(previewFileV2_1), validJsonWithMultipleEmails(jsonFile, 1, {
189+
"0": {
190+
"email_id": 1,
191+
"subject": "The subject line.",
192+
"attachments": [],
193+
"suppress_scheduled": false,
194+
"send_report_as_attachment": false
195+
}
196+
})], {
197+
...cleanupCtx,
198+
env: {
199+
"SPARK_CONNECT_USER_AGENT": "posit-connect/2024.09.0"
200+
}
201+
});
202+
186203
// Test mixed metadata - some emails have metadata, others don't
187204
testRender(docs("email/email-mixed-metadata-v2.qmd"), "email", false, [
188205
fileExists(previewFileV2_1),

0 commit comments

Comments
 (0)