7.2 KiB
Atlassian (Confluence) documentation output
Internal/Documentation/OutputProviders/DocumentationOutputAtlassian.ps1
Emits Confluence storage format (XHTML plus <ac:*> macros) for a documentation
run: paste it into a page's Rich Text -> Source view, or POST it to the Confluence
REST API as representation=storage. Structurally it mirrors the HTML provider
(BasicInfo / FilteredSettings / ComplianceActions / ApplicabilityRules / Assignments /
CustomTables plus a per-run table of contents); only the markup differs.
Select it with -OutputFormat atlassian.
Option ($Options.Outputs.atlassian) |
Default | Meaning |
|---|---|---|
AtlassianDocumentName |
%MyDocuments%\%Organization%-%Date%.html |
Output path. Supports Expand-FileName tokens. |
AtlassianDocumentFileType |
Full |
Full = one file; Object = one file per policy plus a TOC index file. |
AtlassianTitleProperty |
Intune documentation |
<h1> of the index page. |
AtlassianOpenFile |
$true |
Launch the file with the OS default handler. Set $false for CI. |
Heading anchors (a consumer contract)
Every heading is emitted with an inline anchor macro:
<h4 id='section-42'><ac:structured-macro ac:name='anchor' ac:schema-version='1'><ac:parameter ac:name=''>section-42</ac:parameter></ac:structured-macro>Contoso Reader</h4>
The anchor name has appeared as literal <!--#section-N--> text downstream of the
generated file. The source of that rewrite is still unproven: the publishing runbook
parses the body with mshtml before POSTing, and Confluence also transforms storage
format. The inline placement remains the known baseline until those paths are measured
separately.
Three properties are guaranteed, and downstream tooling depends on them:
- The
anchormacro carries the link target. Confluence discards author-specifiedid=attributes when it converts storage format to ADF, so#section-42can only resolve to an anchor macro or to Confluence's own heading-text-derived anchor. id=always equals the macro name, byte for byte. Confluence strips it, but automation that parses the generated file before publishing reads the anchor from it - for example to walk an assignment table cell back to its policy heading and build a deep link into the published page.- The table of contents links these names, never the heading text. Anchor names contain no character that came from tenant data.
Naming scheme (breaking-change surface): section-N for every heading, numbered
in emission order over the whole run. section-N is unique across a run even in
Object mode, because the counter is reset only in
Invoke-AtlassianPreProcessItems. Numbering is not contiguous within the TOC: a
heading consumes a number whenever it is written to the document, including the
level-6 table captions that the TOC's level cap (4) filters out and the -SkipTOC
script captions it never lists. Do not assume contiguity - and do not reconstruct the
numbers by counting TOC entries.
table-N is reserved for the -ToT caption form of Add-AtlassianHeader, which no
call site in this provider uses: table captions here are plain level-6 headings, as
in the HTML provider. Only the Markdown provider passes -ToT (and so is the only
output with visible Table N. numbering). Enabling it for Atlassian would change
rendered captions, so it belongs with the HTML provider as one formatting decision
rather than a port detail.
The scheme is positional, so inserting one policy shifts every later anchor: links a
user saved from an earlier run of a scheduled export therefore move. A content-derived
scheme (policy-<objectId> from the Graph GUID) would be stable and is the natural
follow-up; it needs Add-AtlassianHeader to accept an explicit anchor from
Invoke-AtlassianProcessItem, with the positional counter kept as the fallback for
headers that have no natural identifier.
Deployment ordering: update the module wherever the export runs before a consumer
switches to reading id= / linking #section-N, or its links will point at anchors
that do not exist yet.
Markup constraints
- Single-quote every attribute. Consumers JSON-escape the document body before a
Confluence client serialises it again; a double-quoted attribute arrives as
ac:name=\"anchor\"and breaks the macro. Every macro in the provider follows this, so the body survives a JSON round-trip byte-identically. - Storage format is strict XML and declares only the five XML built-in entities.
Use numeric references (
 , never ), close every macro, and escape text that comes from the tenant -Get-AtlassianXmlTextfor headings, TOC labels, the title and the document-info lines;Set-AtlassianTextfor table cell values (it also wraps XML-looking values in acodemacro and long text in anexpand). A single bare&in a policy name invalidates the whole page body, not one heading, and Confluence rejects the upload. - In
Objectmode the TOC'shrefcarries a file name derived from a policy name.Get-AtlassianObjectFileNamestrips only path-invalid characters, so&,'and#survive into it.Get-AtlassianHrefpercent-encodes the file-name component (never the#that introduces the fragment) and then XML-escapes the result; use it rather than interpolating a file name into an attribute.
Change history
Unreleased (part of 4.0, no shipped version has the earlier behaviour):
- Table of contents entries link the anchor macro each heading emits. They used to
target a fragment re-derived from the heading text, so duplicate policy names all
jumped to the first occurrence and characters other than a plain space (
.,(,),:,&,+,,, U+00A0) leaked into the href unencoded. - Headings carry an inline
anchormacro; previously they carried only anid=that Confluence discards, which nothing could link to. The shape of an anchor name is unchanged (section-N), so a consumer readingid=needs no update. A provisional preceding-paragraph placement was reverted before release because its downstream behavior and blank-line cost had not been measured. - A heading kept out of the TOC now consumes an anchor number. It used to reuse the
next listed heading's number, so a document containing script captions (Detection
script, Requirement scripts) emitted two
section-Nanchors with one name and the TOC entry landed on the caption. This shifts the numbers in such documents: another reason to readid=rather than computesection-Nfrom a position. - Object-mode
hreffile names are percent-encoded. A policy name containing&or'used to produce a malformed body, and one containing#a link to the wrong fragment. - In the
-ToTcaption path (present but unused, see above) theTable N.prefix moved from the id to the visible text, where the Markdown provider puts it. - Heading text, TOC labels, the title and the document-info lines are XML-escaped.
- Confluence keeps generating its own text-derived anchors, so externally saved
#Policy-Namelinks still resolve.
The three guarantees above are covered by the project's own test suite.