Wiki source code of Confluence reference and URL handling
Last modified by Raphaël Jakse on 2026/03/23 13:00
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | {{warning}} | ||
| 2 | This document probably needs to be split into several documents, and also some of it probably needs to be moved to the [[dev documentation>>doc:documentation.extensions.dev.confluence.WebHome]]. | ||
| 3 | {{/warning}} | ||
| 4 | |||
| 5 | {{toc/}} | ||
| 6 | |||
| 7 | == Introduction == | ||
| 8 | |||
| 9 | XWiki and Confluence reference documents quite differently. XWiki references document using their full names, whereas Confluence have several ways of doing this: | ||
| 10 | |||
| 11 | * from its space key and its page title, in a case insensitive way | ||
| 12 | * from its (stable) page id | ||
| 13 | * using special ##@## keywords like ##@home##, ##@self##, ##@parent##. | ||
| 14 | |||
| 15 | Spaces can also be referenced, through their space key or through special keywords like ##currentSpace()## (and actually, since we migrated Confluence space home pages to the target XWiki space's WebHome page, ##@home## requires exactly the same resolution as ##currentSpace()##) | ||
| 16 | |||
| 17 | Attachments are occasionally referenced through their id as well (which we mostly don't currently handle). | ||
| 18 | |||
| 19 | This makes things difficult to handle at import time as well as when displaying the imported content, that may reference documents and spaces in macro parameters (including inside [[CQL queries>>extensions:Extension.CQL.WebHome]]) and links we failed to convert. | ||
| 20 | |||
| 21 | In this document, we describe how we handle Confluence references and what are our strategies for tackling the difficulties | ||
| 22 | |||
| 23 | {{info}} | ||
| 24 | We can sometime see the use of ##@global## or ##@all## in Confluence macro parameters. They respectively refer to "the wiki globally", or "all the spaces". We don't have an equivalent in XWiki. We don't currently have a feature to list all the migrated spaces (although it should not be impossible to do), and it's not even sure it would be the right thing to do (maybe the related feature ought to take other XWiki content in account as well). As for using the main wiki, it would only work if nothing is imported into subwikis. | ||
| 25 | {{/info}} | ||
| 26 | |||
| 27 | == Confluence references == | ||
| 28 | |||
| 29 | === Overview === | ||
| 30 | |||
| 31 | Sometimes, Confluence XML can't convert a Confluence reference to a proper XWiki reference. Usually, that's because we lack the information needed to build the correct reference. This information is: | ||
| 32 | |||
| 33 | * where the space in which the referenced page lives is migrated (it is not necessarily at the root of the main wiki, because of the [[root parameter>>documentation.extensions.admin.confluence.input-parameters.WebHome]]). | ||
| 34 | * the hierarchy between the root of the space and the page (all its ancestor pages) | ||
| 35 | |||
| 36 | This information can be missing when, for instance, a link references a page in a space that has not been imported yet. | ||
| 37 | |||
| 38 | In this case, Confluence XML issues references. There are currently 3 kinds of Confluence references: | ||
| 39 | |||
| 40 | * ##confluenceSpace##: a reference to a confluence space, to be converted to the XWiki space reference where the Confluence space was imported | ||
| 41 | * ##confluencePage##: a reference to a confluence page, to be converted to the corresponding XWiki document reference | ||
| 42 | ** ##confluencePage:page:SPACE.PAGETITLE## for a page with the given space key and page title | ||
| 43 | ** ##confluencePage:id:ID@FILENAME## for a page with the given page id | ||
| 44 | * ##confluenceAttach##: a reference to a Confluence attachment | ||
| 45 | ** ##confluenceAttach:spaceHome:SPACE@FILENAME## for an attachment on the home page of the given space | ||
| 46 | ** ##confluenceAttach:page:SPACE.PAGETITLE@FILENAME## for an attachment on a page with the given space key and page title | ||
| 47 | ** ##confluenceAttach:id:ID@FILENAME## for an attachment on a page with the given page id | ||
| 48 | |||
| 49 | ==== Current implementation ==== | ||
| 50 | |||
| 51 | Confluence references are currently implemented in the [[##confluence-resource-reference-type-parsers## module>>url:https://github.com/xwiki-contrib/confluence/blob/master/confluence-resource-reference-type-parsers/]] as resource reference type parsers, which means that they are transparently converted to XWiki reference whenever possible when parsing a XWiki document. This has a few implications: | ||
| 52 | |||
| 53 | * successfully converted Confluence references do not show up in the the document's XDOM, it's like if the corresponding XWiki references were always there and not the Confluence references. | ||
| 54 | * parsing a XWiki document with Confluence references is potentially costly, because confluence resolvers are called and Solr queries are fired by the default confluence resolvers | ||
| 55 | * when saving a document containing successfully converted Confluence references, the conversions will happen automatically (documents are somehow automatically "fixed"), and the conversions will appear in the diff. | ||
| 56 | |||
| 57 | ==== Alternative implementation ==== | ||
| 58 | |||
| 59 | It would be possible to handle Confluence references differently and have it work anyway. For instance, instead of implementing resource reference type parsers, we could have implemented [[##XHTMLLinkRenderer##>>https://github.com/xwiki/xwiki-rendering/tree/master/xwiki-rendering-syntaxes/xwiki-rendering-syntax-xhtml/src/main/java/org/xwiki/rendering/internal/renderer/xhtml/link/XHTMLLinkRenderer.java]], which converts links when rendering to HTML, not when not parsing the XDOM. This has a few (blocking) drawbacks: | ||
| 60 | |||
| 61 | * ##XHTMLLinkRenderer## is internal and should not be relied upon has since it's internal, it could disappear | ||
| 62 | * The pages won't self heal when edited and saved, the confluence references are staying (although it can be seen as an advantage too) | ||
| 63 | * The handling is specific to the HTML output, you would need to implement something similar and specific for each output format | ||
| 64 | |||
| 65 | Should you want to go ahead and implement such a HTML link renderer, you could take inspiration from [[this untested implementation>>attach:ConfluencePageXHTMLLinkTypeRenderer.java]]. You will also need to provide a minimal, alternative implementation for them [[with a higher priority to override>>extensions:Extension.Component Module||anchor="HOverrides"]] the standard ones (which you can also make sure not to install), such as: | ||
| 66 | |||
| 67 | {{code language="java"}} | ||
| 68 | @Component | ||
| 69 | @Named("confluenceAttach") | ||
| 70 | @Singleton | ||
| 71 | public class CustomConfluenceAttachResourceReferenceTypeParser implements ResourceReferenceTypeParser | ||
| 72 | { | ||
| 73 | private static final ResourceType CONFLUENCE_ATTACH = new ResourceType("confluenceAttach"); | ||
| 74 | |||
| 75 | @Override | ||
| 76 | public ResourceType getType() | ||
| 77 | { | ||
| 78 | return CONFLUENCE_ATTACH; | ||
| 79 | } | ||
| 80 | |||
| 81 | @Override | ||
| 82 | public ResourceReference parse(String reference) | ||
| 83 | { | ||
| 84 | return new ResourceReference(reference, CONFLUENCE_ATTACH); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | {{/code}} | ||
| 88 | |||
| 89 | This would need to be done for each of the three Confluence reference types. | ||
| 90 | |||
| 91 | ==== Getting rid of Confluence references ==== | ||
| 92 | |||
| 93 | It's not nice to have Confluence references in XWiki content. They do mostly work, but they are costly, are not handled by a lot of stuff and are simply not idiomatic. We also do issue Confluence references at places where they are not supported, like in the reference parameter of the display macro. | ||
| 94 | |||
| 95 | We advise getting rid of Confluence when possible. No tool exists for this in standard XWiki or in a contrib extension, but the rough idea on how to do it is: | ||
| 96 | |||
| 97 | * Browse your documents and parse them as XDOM | ||
| 98 | * In each document, look for references beginning with confluencePage:, confluenceSpace: or confluenceAttach: inside references or macro parameters | ||
| 99 | * Parse them, following the description detailed in the overview section | ||
| 100 | * Call the confluence resolvers | ||
| 101 | * Update the document | ||
| 102 | |||
| 103 | {{info}} | ||
| 104 | If you have the Confluence resource reference type parsers installed, there is no need to browse references, because the ones that can be fixed are fixed at parse time. You only need to look inside macro parameters. To know whether you need to save documents, you can compare the number of occurrence of "confluence" in the source, and then in the serialized XDOM. | ||
| 105 | {{/info}} | ||
| 106 | |||
| 107 | {{info}} | ||
| 108 | You may also want to find Confluence absolute URLs and to try converting them as well | ||
| 109 | {{/info}} | ||
| 110 | |||
| 111 | (% class="wikigeneratedid" %) | ||
| 112 | You can take inspiration from [[this implementation>>https://github.com/xwikisas/application-confluence-migrator-pro/blob/main/application-confluence-migrator-pro-reference-fixer/application-confluence-migrator-pro-reference-fixer-api/src/main/java/com/xwiki/confluencepro/referencefixer/internal/ConfluenceReferenceFixer.java]]. | ||
| 113 | |||
| 114 | == Confluence resolvers == | ||
| 115 | |||
| 116 | We do our best not to rely on the way Confluence references documents and spaces after importing the content, but that's not completely avoidable: | ||
| 117 | |||
| 118 | * often, it is not possible to convert links at import times. | ||
| 119 | * Some macros and bridges reference Confluence stuff the Confluence way (space key + page title, or id, or special keywords). Including macros using [[CQL>>https://github.com/xwikisas/application-confluence-migrator-pro/blob/main/application-confluence-migrator-pro-reference-fixer/application-confluence-migrator-pro-reference-fixer-api/src/main/java/com/xwiki/confluencepro/referencefixer/internal/ConfluenceReferenceFixer.java]], which has such properties as "ancestor", "space", "parent", "title". | ||
| 120 | * we have a [[strategy to handle old Confluence links>>extensions:Extension.Confluence.URL Mapping.WebHome]] that could be hanging out in people's emails, chat history and bookmarks. | ||
| 121 | |||
| 122 | At import time, links to other spaces also often exist, and resolving links we can resolve at import time is useful too. | ||
| 123 | |||
| 124 | We therefore provide [[interfaces>>https://github.com/xwiki-contrib/confluence/tree/master/confluence-resolvers/src/main/java/org/xwiki/contrib/confluence/resolvers]] to query the wiki for Confluence things. Best is to have a look at the Javadoc, but in short: | ||
| 125 | |||
| 126 | * ##ConfluencePageIdResolver## defines a ##getDocumentById(long id)## method to find a document using its Confluence id | ||
| 127 | * ##ConfluencePageTitleResolver## defines a ##getDocumentByTitle(String spaceKey, String title)## method to find a document using its Confluence space key and Confluence page title | ||
| 128 | * ##ConfluenceSpaceKeyResolver## defines a ##getSpaceByKey(String spaceKey)## method to find where a Confluence space was imported and returns a reference of type ##EntityType.SPACE## | ||
| 129 | * ##ConfluenceSpaceResolver## defines two methods: | ||
| 130 | ** ##getSpace(EntityReference reference)## return the root of the Confluence space which the given document belongs to | ||
| 131 | ** ##getSpaceKey(EntityReference reference)## returns the Confluence space key of the space in which the given document lives | ||
| 132 | |||
| 133 | The [[default implementation of each of these interfaces>>https://github.com/xwiki-contrib/confluence/tree/master/confluence-resolvers/src/main/java/org/xwiki/contrib/confluence/resolvers/internal]] loops over the all the available implementations and stops as soon as one finds a result. | ||
| 134 | |||
| 135 | An [[implementation for all these interfaces relying on ##Confluence.Code.ConfluencePageClass##>>https://github.com/xwiki-contrib/confluence/blob/master/confluence-resolvers/src/main/java/org/xwiki/contrib/confluence/resolvers/internal/PageClassConfluenceResolver.java]] is provided. These objects are, by default, available for each document imported using Confluence XML (but this can be disabled). They store important information like the page id, the space key and the title of documents coming from Confluence. | ||
| 136 | |||
| 137 | This means that by default, you should be fully able to inject these interfaces as components in your own components and query the wiki and expect this to work. | ||
| 138 | |||
| 139 | {{warning}} | ||
| 140 | This won't work with content imported with the "Store confluence details" parameter of Confluence XML set to false. | ||
| 141 | \\This was notably the case for all contents imported before mid-2024, except for content imported using the [[unmaintained application-confluence-migrator>>https://github.com/xwikisas/application-confluence-migrator]] starting from version 1.0-rc-2 released in November 2021. | ||
| 142 | {{/warning}} | ||
| 143 | |||
| 144 | {{info}} | ||
| 145 | Confluence resolvers are designed to be extended and to welcome alternative implementations, as well as to support multiple implementations at the same time. This means that you can provide your custom way of resolving things. For instance, you can write an implementation that uses data from extracted from the Confluence database. You might be able to bring support for content imported a while ago for instance. | ||
| 146 | |||
| 147 | Since Confluence XML calls Confluence resolvers at import time too, you can also help Confluence XML convert links at import time for spaces that have not been imported yet this way, if you know in advance where each space is going to be imported. | ||
| 148 | {{/info}} | ||
| 149 | |||
| 150 | {{warning}} | ||
| 151 | Confluence is generally case insensitive, but the default implementation of the Confluence resolvers [[is case sensitive>>https://jira.xwiki.org/browse/CONFLUENCE-481]]. This is a big limitation that should be easy to lift as soon as we start [[indexing string properties case insensitively in Solr>>https://forum.xwiki.org/t/add-the-ability-to-do-case-insensitive-solr-search-for-stringproperty-and-listproperty/16955]]. | ||
| 152 | |||
| 153 | Confluence resolvers formerly used HQL which does have a ##lower## function, but: | ||
| 154 | |||
| 155 | * this doesn't work well in a multi wiki setting (we would have to loop over all the wikis) | ||
| 156 | * ##lower## ruins performance anyway, because it invalidates the use of indexes | ||
| 157 | {{/warning}} | ||
| 158 | |||
| 159 | === Confluence URL conversion === | ||
| 160 | |||
| 161 | At import time, Confluence XML goes out of it way to convert Confluence absolute URLs to proper XWiki references. | ||
| 162 | |||
| 163 | Like confluence resolvers, this is extensible and outside code can provide URL converters by implementing [[ConfluenceURLConverter>>https://github.com/xwiki-contrib/confluence/tree/master/confluence-syntax-xhtml/src/main/java/org/xwiki/contrib/confluence/parser/xhtml/ConfluenceURLConverter.java]]. Confluence XML provides a convenient base class for doing this, [[AbstractConfluenceURLConverter>>https://github.com/xwiki-contrib/confluence/blob/master/confluence-xml/src/main/java/org/xwiki/contrib/confluence/filter/url/AbstractConfluenceURLConverter.java]]. | ||
| 164 | |||
| 165 | A URL converter implements the convertURL method that takes a strings, and return the corresponding resource reference, or null if the converter does not know how to convert this URL. The [[default implement>>https://github.com/xwiki-contrib/confluence/blob/master/confluence-xml/src/main/java/org/xwiki/contrib/confluence/filter/url/internal/DefaultConfluenceURLConverter.java]] loops over all the available converters and stops as soon as one can convert the URL. Confluence XML comes with [[a converter that converts the widespread standard Confluence URLs>>https://github.com/xwiki-contrib/confluence/blob/master/confluence-xml/src/main/java/org/xwiki/contrib/confluence/filter/url/internal/BaseConfluenceURLConverter.java]]. | ||
| 166 | |||
| 167 | Macro converters can use ConfluenceURLConverter to convert URL. They need to make sure to handle the null case when the URL could not be converted, so as to not fortuitously drop the URL. |