diff --git a/commons/doc-maven-plugin/README.md b/commons/doc-maven-plugin/README.md
index c815c66b6..b811a7495 100644
--- a/commons/doc-maven-plugin/README.md
+++ b/commons/doc-maven-plugin/README.md
@@ -742,6 +742,82 @@ The following example shows the full configuration to use the 2.1.3 version.
If you need to create your own shared content artifact,
see the `forgerock-doc-common-content` project as an example.
+# Documentation site versions in AsciiDoc PDF links
+
+The `asciidoc-to-pdf` goal converts cross-guide and cross-component xrefs to
+links on `siteUrl` (default `https://doc.openidentityplatform.org`). By default,
+these links use the unversioned current documentation. To build a PDF for
+archived documentation, configure the published site version paths explicitly:
+
+```xml
+
+ openam
+ 15.2
+
+ 4.10
+
+
+```
+
+`docVersion` is also available as the Maven property `-DdocVersion=15.2`; a
+`docVersion` project property, including one inherited from a parent POM, sets it too.
+Omit it for current documentation. `componentDocVersions` is a Maven map keyed
+by Antora component name; configure it in the POM. Each value is the exact
+version directory published by the site, not a release tag or Maven version.
+For example, the site's `versions-from-tags.js` maps `OpenAM-15.2.2` to `15.2`
+and `OpenDJ-4.10.2` to `4.10`; its branch sources have no version directory.
+Neither `projectVersion` nor `releaseVersion` determines the site path.
+
+For `xref:reference:chap-auth-modules.adoc#data-store-module-ref[Data Store]`,
+the configuration above produces
+`https://doc.openidentityplatform.org/openam/15.2/reference/chap-auth-modules.html#data-store-module-ref`.
+For `xref:opendj::index.adoc[OpenDJ]`, it produces
+`https://doc.openidentityplatform.org/opendj/4.10/index.html`.
+The `ROOT` module has no module directory. Relative cross-guide xrefs also use
+the current component's configured version; fragments and same-guide xrefs are
+preserved. Only the PDF source copy is rewritten; Antora sources are unchanged.
+
+Version selection, in order:
+
+1. An explicit Antora version in an xref that names a module, such as
+ `xref:4.10@opendj:install-guide:index.adoc[Install]`.
+2. An entry for the target component in `componentDocVersions`.
+3. `docVersion`, only when the target is this project's component.
+4. The unversioned current documentation.
+
+Rule 3 also applies when the xref explicitly names this project's component,
+such as `xref:openam:ROOT:index.adoc[OpenAM]` in an OpenAM PDF. With
+`docVersion=15.2`, the PDF deliberately links to `/openam/15.2/index.html`.
+Antora instead resolves a component-qualified xref without an explicit version
+to the latest version of that component, so the HTML link can differ.
+
+An empty map entry or Antora's versionless token `_` (`xref:_@opendj::index.adoc[Current]`)
+selects the unversioned site. The empty version form `xref:@opendj::index.adoc[Current]`
+is not a valid Antora resource ID and is not converted. A component without an entry never inherits
+another component's version.
+
+Version-only resource IDs such as `xref:15.1@page.adoc[Older]` are left unchanged.
+To generate a site link in the PDF, name the target module explicitly, for example
+`xref:15.1@how-to:page.adoc[Older]`. This also avoids depending on the including
+page's module when the xref occurs in a shared partial.
+
+Rebuilding requires a doc-maven-plugin release that supports `docVersion` and
+`componentDocVersions`; these options are unavailable in 3.2.2 and earlier.
+Release POMs that pin the plugin version, such as OpenDJ 4.10.2 and 5.2.0 through
+`${commons.version}`, must select a supporting plugin version for the rebuild.
+Passing `-DdocVersion` alone does not upgrade the plugin; an older plugin ignores
+the property and keeps unversioned links.
+
+When a release moves from current to archived documentation, first publish and
+verify its historical site paths, then rebuild its PDF from the immutable
+release sources using the corresponding `docVersion` and independently chosen
+component versions. For a POM with the AsciiDoc preprocessing and PDF goals
+bound to the site lifecycle, use `mvn -DdocVersion=15.2 site`; otherwise run the
+project's normal PDF build with that property. Replace the archived PDF with
+the rebuilt artifact. Previously distributed PDFs retain their original links;
+this converter cannot update them. Do not select a historical version path
+before that version is published on the site.
+
* * *
The contents of this file are subject to the terms of the Common Development and
@@ -757,3 +833,4 @@ Header, with the fields enclosed by brackets [] replaced by your own identifying
information: "Portions copyright [year] [name of copyright owner]".
Copyright 2012-2015 ForgeRock AS.
+Portions copyright 2026 3A Systems, LLC.
diff --git a/commons/doc-maven-plugin/src/main/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojo.java b/commons/doc-maven-plugin/src/main/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojo.java
index e4adcaea6..6bfae7031 100644
--- a/commons/doc-maven-plugin/src/main/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojo.java
+++ b/commons/doc-maven-plugin/src/main/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojo.java
@@ -11,8 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
- * Copyright 2024-2026 3A Systems LLC.
- * Portions Copyright 2026 3A Systems, LLC.
+ * Copyright 2024-2026 3A Systems, LLC.
*/
package org.openidentityplatform.doc.maven;
@@ -35,8 +34,10 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -79,6 +80,21 @@ public AsciidocToPdfMojo() {
@Parameter(property = "antoraComponent")
private String antoraComponent;
+ /**
+ * Site version path for this component, such as {@code 15.2}. Empty means the
+ * unversioned current documentation. This is independent of the Maven project version.
+ */
+ @Parameter(property = "docVersion")
+ private String docVersion;
+
+ /**
+ * Site version paths by Antora component name. Entries override {@code docVersion}
+ * for that component; other components never inherit this project's version.
+ * An empty entry selects the unversioned current documentation.
+ */
+ @Parameter
+ private Map componentDocVersions = Collections.emptyMap();
+
public File getPdfOutputDirectory() {
return new File(buildDirectory, "/pdf");
}
@@ -93,7 +109,8 @@ public void execute() throws MojoExecutionException, MojoFailureException {
String component = antoraComponent != null && !antoraComponent.isEmpty()
? antoraComponent : projectName.toLowerCase(Locale.ROOT);
try {
- preparePdfSource(getAsciidocBuildSourceDirectory(), getPdfSourceDirectory(), siteUrl, component);
+ preparePdfSource(getAsciidocBuildSourceDirectory(), getPdfSourceDirectory(), siteUrl, component,
+ docVersion, componentDocVersions);
} catch (IOException e) {
throw new MojoExecutionException("error preparing PDF sources", e);
}
@@ -151,6 +168,11 @@ public void execute() throws MojoExecutionException, MojoFailureException {
* so that the sources the {@code antora} goal reads stay as they are.
*/
static void preparePdfSource(File source, File target, String siteUrl, String component) throws IOException {
+ preparePdfSource(source, target, siteUrl, component, null, Collections.emptyMap());
+ }
+
+ static void preparePdfSource(File source, File target, String siteUrl, String component,
+ String docVersion, Map componentDocVersions) throws IOException {
if (target.exists()) {
FileUtils.deleteDirectory(target);
}
@@ -158,7 +180,7 @@ static void preparePdfSource(File source, File target, String siteUrl, String co
for (File adocFile : FileUtils.listFiles(target, new String[] {"adoc"}, true)) {
String module = adocFile.getParentFile().getName();
String adoc = FileUtils.readFileToString(adocFile, StandardCharsets.UTF_8);
- String converted = convertXrefsForPdf(adoc, siteUrl, component, module);
+ String converted = convertXrefsForPdf(adoc, siteUrl, component, module, docVersion, componentDocVersions);
if (!converted.equals(adoc)) {
FileUtils.writeStringToFile(adocFile, converted, StandardCharsets.UTF_8);
}
@@ -166,7 +188,8 @@ static void preparePdfSource(File source, File target, String siteUrl, String co
}
private static final Pattern XREF_PATTERN = Pattern.compile("xref:([^\\[\\s]+)\\[");
- private static final Pattern RESOURCE_ID_PATTERN = Pattern.compile("(?:([^:/@$]+):)?([^:/@$]*):([^:@$]+)");
+ private static final Pattern RESOURCE_ID_PATTERN =
+ Pattern.compile("(?:([^:/@$]+)@)?(?:([^:/@$]+):)?([^:/@$]*):([^:@$]+)");
/**
* Plain Asciidoctor knows neither the other guides nor the other Antora components, so it
@@ -176,27 +199,38 @@ static void preparePdfSource(File source, File target, String siteUrl, String co
* {@code xref:component:module:page.adoc#anchor[text]} and {@code xref:module:page.adoc#anchor[text]}
* {@code xref:../module/page.adoc#anchor[text]}
*
+ * Explicit {@code version@component:module:page.adoc} resource IDs override configured
+ * component versions; an explicit {@code _} version selects the unversioned site.
+ * Resource IDs without a module coordinate, such as {@code version@page.adoc}, are left unchanged.
* A relative xref into {@code currentModule} becomes a same-guide xref. Other xrefs are left as they are.
*/
static String convertXrefsForPdf(String adoc, String siteUrl, String component, String currentModule) {
+ return convertXrefsForPdf(adoc, siteUrl, component, currentModule, null, Collections.emptyMap());
+ }
+
+ static String convertXrefsForPdf(String adoc, String siteUrl, String component, String currentModule,
+ String docVersion, Map componentDocVersions) {
String site = siteUrl.replaceAll("/+$", "");
Matcher m = XREF_PATTERN.matcher(adoc);
StringBuilder builder = new StringBuilder();
int i = 0;
while (m.find()) {
builder.append(adoc, i, m.start());
- builder.append(convertXrefForPdf(m.group(1), site, component, currentModule)).append("[");
+ builder.append(convertXrefForPdf(m.group(1), site, component, currentModule,
+ docVersion, componentDocVersions)).append("[");
i = m.end();
}
builder.append(adoc.substring(i));
return builder.toString();
}
- private static String convertXrefForPdf(String target, String site, String component, String currentModule) {
+ private static String convertXrefForPdf(String target, String site, String component, String currentModule,
+ String docVersion, Map componentDocVersions) {
int hash = target.indexOf('#');
String path = hash < 0 ? target : target.substring(0, hash);
String fragment = hash < 0 ? "" : target.substring(hash);
+ String explicitVersion = null;
String targetComponent;
String module;
String page;
@@ -217,16 +251,28 @@ private static String convertXrefForPdf(String target, String site, String compo
if (!id.matches()) {
return "xref:" + target;
}
- targetComponent = id.group(1) != null ? id.group(1) : component;
- module = id.group(2).isEmpty() ? "ROOT" : id.group(2);
- page = id.group(3);
+ explicitVersion = id.group(1);
+ // Antora uses _ to explicitly select the versionless component version.
+ if ("_".equals(explicitVersion)) {
+ explicitVersion = "";
+ }
+ targetComponent = id.group(2) != null ? id.group(2) : component;
+ module = id.group(3).isEmpty() ? "ROOT" : id.group(3);
+ page = id.group(4);
}
if (page.isEmpty()) {
return "xref:" + target;
}
+ String version = explicitVersion;
+ if (version == null) {
+ version = componentDocVersions.containsKey(targetComponent)
+ ? componentDocVersions.get(targetComponent)
+ : targetComponent.equals(component) ? docVersion : null;
+ }
+ String versionPrefix = version == null || version.isEmpty() ? "" : version + "/";
page = page.replaceAll("\\.adoc$", "");
// Antora publishes the pages of the ROOT module directly under the component
String modulePrefix = module.equals("ROOT") ? "" : module + "/";
- return "link:" + site + "/" + targetComponent + "/" + modulePrefix + page + ".html" + fragment;
+ return "link:" + site + "/" + targetComponent + "/" + versionPrefix + modulePrefix + page + ".html" + fragment;
}
}
diff --git a/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoConfigurationTest.java b/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoConfigurationTest.java
new file mode 100644
index 000000000..31e6eadc7
--- /dev/null
+++ b/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoConfigurationTest.java
@@ -0,0 +1,118 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2026 3A Systems, LLC.
+ */
+
+package org.openidentityplatform.doc.maven;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import java.io.File;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AsciidocToPdfMojoConfigurationTest extends AbstractMojoTestCase {
+
+ private File buildDirectory;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ buildDirectory = Files.createTempDirectory("pdf-configuration").toFile();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ FileUtils.deleteDirectory(buildDirectory);
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ public void testEmptyPomEntryOverridesProjectVersion() throws Exception {
+ MavenProject project = readProject();
+ AsciidocToPdfMojo mojo = (AsciidocToPdfMojo) lookupConfiguredMojo(project, "asciidoc-to-pdf");
+ assertPreparedLinks(mojo, "");
+ }
+
+ public void testPomVersionsReachPreparedPdfSource() throws Exception {
+ MavenProject project = readProject();
+ Xpp3Dom configuration = (Xpp3Dom) project.getBuildPlugins().get(0).getConfiguration();
+ Xpp3Dom versions = configuration.getChild("componentDocVersions");
+ versions.removeChild(versions.getChild("openam"));
+ AsciidocToPdfMojo mojo = (AsciidocToPdfMojo) lookupConfiguredMojo(project, "asciidoc-to-pdf");
+ assertPreparedLinks(mojo, "15.2/");
+ }
+
+ public void testMavenUserPropertySelectsHistoricalVersion() throws Exception {
+ MavenProject project = readProject();
+ Xpp3Dom configuration = (Xpp3Dom) project.getBuildPlugins().get(0).getConfiguration();
+ configuration.removeChild(configuration.getChild("docVersion"));
+ Xpp3Dom versions = configuration.getChild("componentDocVersions");
+ versions.removeChild(versions.getChild("openam"));
+ MavenSession session = newMavenSession(project);
+ session.getUserProperties().setProperty("docVersion", "15.2");
+ AsciidocToPdfMojo mojo = (AsciidocToPdfMojo) lookupConfiguredMojo(session,
+ newMojoExecution("asciidoc-to-pdf"));
+ assertPreparedLinks(mojo, "15.2/");
+ }
+
+ public void testComponentDocVersionsIsADescriptorParameter() throws Exception {
+ assertTrue(newMojoExecution("asciidoc-to-pdf").getMojoDescriptor()
+ .getParameterMap().containsKey("componentDocVersions"));
+ }
+
+ public void testAbsentMapKeepsOtherComponentsUnversioned() throws Exception {
+ MavenProject project = readProject();
+ Xpp3Dom configuration = (Xpp3Dom) project.getBuildPlugins().get(0).getConfiguration();
+ configuration.removeChild(configuration.getChild("componentDocVersions"));
+ AsciidocToPdfMojo mojo = (AsciidocToPdfMojo) lookupConfiguredMojo(project, "asciidoc-to-pdf");
+ FileUtils.writeStringToFile(new File(mojo.getAsciidocBuildSourceDirectory(), "how-to/page.adoc"),
+ "xref:reference:page.adoc[A] xref:opendj::index.adoc[B]", StandardCharsets.UTF_8);
+
+ mojo.execute();
+
+ assertThat(new File(mojo.getPdfSourceDirectory(), "how-to/page.adoc")).hasContent(
+ "link:https://doc.openidentityplatform.org/openam/15.2/reference/page.html[A] "
+ + "link:https://doc.openidentityplatform.org/opendj/index.html[B]");
+ }
+
+ private MavenProject readProject() throws Exception {
+ File pom = getTestFile("src/test/resources/asciidoc-to-pdf/pom.xml");
+ try (Reader reader = Files.newBufferedReader(pom.toPath(), StandardCharsets.UTF_8)) {
+ MavenProject project = new MavenProject(new MavenXpp3Reader().read(reader));
+ project.setFile(pom);
+ project.getBuild().setDirectory(buildDirectory.getAbsolutePath());
+ return project;
+ }
+ }
+
+ private void assertPreparedLinks(AsciidocToPdfMojo mojo, String versionPrefix) throws Exception {
+ FileUtils.writeStringToFile(new File(mojo.getAsciidocBuildSourceDirectory(), "how-to/page.adoc"),
+ "xref:reference:page.adoc#a[A] xref:opendj::index.adoc[B]", StandardCharsets.UTF_8);
+ mojo.execute();
+ assertThat(new File(mojo.getPdfSourceDirectory(), "how-to/page.adoc")).hasContent(
+ "link:https://doc.openidentityplatform.org/openam/" + versionPrefix + "reference/page.html#a[A] "
+ + "link:https://doc.openidentityplatform.org/opendj/4.10/index.html[B]");
+ }
+}
diff --git a/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoTest.java b/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoTest.java
index ca7a21140..30f0cde1b 100644
--- a/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoTest.java
+++ b/commons/doc-maven-plugin/src/test/java/org/openidentityplatform/doc/maven/AsciidocToPdfMojoTest.java
@@ -22,7 +22,12 @@
import org.junit.rules.TemporaryFolder;
import java.io.File;
+import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@@ -37,6 +42,152 @@ private static String convert(String adoc) {
return AsciidocToPdfMojo.convertXrefsForPdf(adoc, SITE, "openidm", "getting-started");
}
+ @Test
+ public void testExplicitVersionedComponentXref() {
+ assertThat(convert("xref:4.8@opendj:admin-guide:chap-replication.adoc#replication[Replication]"))
+ .isEqualTo("link:" + SITE + "/opendj/4.8/admin-guide/chap-replication.html#replication[Replication]");
+ }
+
+ @Test
+ public void testHistoricalCrossGuideLinksPreserveFragments() {
+ String adoc = "xref:reference:chap-auth-modules.adoc#data-store-module-ref[Data Store] "
+ + "xref:../reference/chap-auth-modules.adoc#data-store-module-ref[Data Store]";
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(adoc, SITE, "openam", "how-to", "15.2",
+ Collections.emptyMap())).isEqualTo(
+ "link:" + SITE + "/openam/15.2/reference/chap-auth-modules.html#data-store-module-ref[Data Store] "
+ + "link:" + SITE + "/openam/15.2/reference/chap-auth-modules.html#data-store-module-ref[Data Store]");
+ }
+
+ @Test
+ public void testOtherComponentDoesNotInheritProjectVersion() {
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf("xref:opendj::index.adoc#overview[OpenDJ]",
+ SITE, "openam", "how-to", "15.2", Collections.emptyMap()))
+ .isEqualTo("link:" + SITE + "/opendj/index.html#overview[OpenDJ]");
+ }
+
+ @Test
+ public void testComponentVersionsAreIndependentAndRootHasNoModuleSegment() {
+ Map versions = Collections.singletonMap("opendj", "4.8");
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(
+ "xref:opendj::index.adoc#overview[OpenDJ] xref:openam:ROOT:index.adoc[OpenAM] "
+ + "xref:opendj:install-guide:index.adoc[Install]",
+ SITE, "openam", "how-to", "15.2", versions)).isEqualTo(
+ "link:" + SITE + "/opendj/4.8/index.html#overview[OpenDJ] "
+ + "link:" + SITE + "/openam/15.2/index.html[OpenAM] "
+ + "link:" + SITE + "/opendj/4.8/install-guide/index.html[Install]");
+ }
+
+ @Test
+ public void testExplicitVersionOverridesConfiguredComponentVersion() {
+ Map versions = new HashMap<>();
+ versions.put("opendj", "4.8");
+ versions.put("openam", "15.1");
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(
+ "xref:4.7@opendj:ROOT:index.adoc[Older] xref:15.0@reference:page.adoc#a[Reference] "
+ + "xref:reference:page.adoc[Configured] xref:_@opendj::index.adoc[Current]",
+ SITE, "openam", "how-to", "15.2", versions)).isEqualTo(
+ "link:" + SITE + "/opendj/4.7/index.html[Older] "
+ + "link:" + SITE + "/openam/15.0/reference/page.html#a[Reference] "
+ + "link:" + SITE + "/openam/15.1/reference/page.html[Configured] "
+ + "link:" + SITE + "/opendj/index.html[Current]");
+ }
+
+ @Test
+ public void testEmptyExplicitVersionIsNotAnAntoraResourceId() {
+ String adoc = "xref:@opendj::index.adoc[Current] xref:@reference:page.adoc[A] xref:@page.adoc[B]";
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(adoc, SITE, "openam", "how-to", "15.2",
+ Collections.emptyMap())).isEqualTo(adoc);
+ }
+
+ @Test
+ public void testExplicitVersionWithoutModuleIsUnchanged() {
+ String adoc = "xref:15.1@page.adoc#a[Older] xref:_@page.adoc#b[Current]";
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(adoc, SITE, "openam", "how-to", "15.2",
+ Collections.singletonMap("openam", "15.0"))).isEqualTo(adoc);
+ }
+
+ @Test
+ public void testEmptyComponentEntriesOverrideProjectVersion() {
+ Map versions = new HashMap<>();
+ for (String empty : new String[] {null, ""}) {
+ versions.put("openam", empty);
+ versions.put("opendj", empty);
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(
+ "xref:reference:page.adoc[A] xref:opendj::index.adoc[B]",
+ SITE, "openam", "how-to", "15.2", versions)).isEqualTo(
+ "link:" + SITE + "/openam/reference/page.html[A] "
+ + "link:" + SITE + "/opendj/index.html[B]");
+ }
+ }
+
+ @Test
+ public void testConfiguredVersionsAreLiteralSitePaths() {
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(
+ "xref:reference:page.adoc[A] xref:opendj::index.adoc[B]",
+ SITE, "openam", "how-to", "_", Collections.singletonMap("opendj", "_"))).isEqualTo(
+ "link:" + SITE + "/openam/_/reference/page.html[A] "
+ + "link:" + SITE + "/opendj/_/index.html[B]");
+ }
+
+ @Test
+ public void testHistoricalSameGuideLinksStayInternal() {
+ String adoc = "xref:../how-to/page.adoc#a[A] xref:#local[Local] xref:page.adoc[Page]";
+ assertThat(AsciidocToPdfMojo.convertXrefsForPdf(adoc, SITE, "openam", "how-to", "15.2",
+ Collections.emptyMap())).isEqualTo("xref:page.adoc#a[A] xref:#local[Local] xref:page.adoc[Page]");
+ }
+
+ @Test
+ public void testHistoricalPreparePdfSourceUsesConfiguredVersions() throws Exception {
+ File source = tmp.newFolder("historical-source");
+ File chapter = new File(source, "how-to/page.adoc");
+ String original = "xref:reference:chap-auth-modules.adoc#data-store-module-ref[Data Store] "
+ + "xref:opendj::index.adoc[OpenDJ] xref:#local[Local]\n";
+ FileUtils.writeStringToFile(chapter, original, StandardCharsets.UTF_8);
+ File target = new File(tmp.getRoot(), "historical-pdf-source");
+ FileUtils.writeStringToFile(new File(target, "stale.adoc"), "obsolete", StandardCharsets.UTF_8);
+
+ AsciidocToPdfMojo.preparePdfSource(source, target, SITE + "/", "openam", "15.2",
+ Collections.singletonMap("opendj", "4.8"));
+
+ assertThat(FileUtils.readFileToString(chapter, StandardCharsets.UTF_8)).isEqualTo(original);
+ assertThat(new File(target, "how-to/page.adoc"))
+ .hasContent("link:" + SITE
+ + "/openam/15.2/reference/chap-auth-modules.html#data-store-module-ref[Data Store] "
+ + "link:" + SITE + "/opendj/4.8/index.html[OpenDJ] xref:#local[Local]");
+ assertThat(new File(target, "stale.adoc")).doesNotExist();
+ }
+
+ @Test
+ public void testExecutePassesDocumentationVersionConfigurationToSourcePreparation() throws Exception {
+ AsciidocToPdfMojo mojo = new AsciidocToPdfMojo() {
+ @Override
+ protected List getDocuments() {
+ return Collections.singletonList("another-guide");
+ }
+ };
+ mojo.buildDirectory = tmp.newFolder("execute-build");
+ mojo.projectName = "OpenAM";
+ setParameter(mojo, "siteUrl", SITE);
+ setParameter(mojo, "docVersion", "15.2");
+ setParameter(mojo, "componentDocVersions", Collections.singletonMap("opendj", "4.10"));
+ FileUtils.writeStringToFile(new File(mojo.getAsciidocBuildSourceDirectory(), "how-to/page.adoc"),
+ "xref:reference:chap-auth-modules.adoc#data-store-module-ref[Data Store] "
+ + "xref:opendj::index.adoc[OpenDJ]", StandardCharsets.UTF_8);
+
+ mojo.execute();
+
+ assertThat(new File(mojo.getPdfSourceDirectory(), "how-to/page.adoc"))
+ .hasContent("link:" + SITE
+ + "/openam/15.2/reference/chap-auth-modules.html#data-store-module-ref[Data Store] "
+ + "link:" + SITE + "/opendj/4.10/index.html[OpenDJ]");
+ }
+
+ private static void setParameter(AsciidocToPdfMojo mojo, String name, Object value) throws Exception {
+ Field parameter = AsciidocToPdfMojo.class.getDeclaredField(name);
+ parameter.setAccessible(true);
+ parameter.set(mojo, value);
+ }
+
@Test
public void testCrossComponentXref() {
assertThat(convert("see xref:opendj:install-guide:index.adoc[Install Guide]."))
diff --git a/commons/doc-maven-plugin/src/test/resources/asciidoc-to-pdf/pom.xml b/commons/doc-maven-plugin/src/test/resources/asciidoc-to-pdf/pom.xml
new file mode 100644
index 000000000..d5833d77d
--- /dev/null
+++ b/commons/doc-maven-plugin/src/test/resources/asciidoc-to-pdf/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ 4.0.0
+ org.openidentityplatform.commons
+ pdf-configuration-test
+ 1.0.0
+ pom
+
+
+
+ org.openidentityplatform.commons
+ doc-maven-plugin
+
+ OpenAM
+ 15.2
+
+ 4.10
+
+
+
+ another-guide
+
+
+
+
+
+