Wiki source code of UI Extension Points
Last modified by Eleni Cojocariu on 2026/10/09 15:14
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
![]() |
2.1 | 1 | A UI extension point (UIXP) is a named place in a rendered page that an extension can add content to without owning the code that renders it, a mechanism the [[UIExtension Module>>doc:extensions:Extension.UIExtension Module]] provides to the whole platform. The Applications Panel, the tabs below a page and the user menu are built this way: each one is an id, and what it shows is whatever is registered against that id when the page is rendered. |
| 2 | |||
| 3 | == Nothing Declares a Point == | ||
| 4 | |||
| 5 | A point exists because something asks for its contributions by id. From wiki syntax, which inserts them one after another: | ||
| 6 | |||
| 7 | {{code language="none"}} | ||
| 8 | {{uiextensions extensionPoint="org.xwiki.platform.panels.Applications"/}} | ||
| 9 | {{/code}} | ||
| 10 | |||
| 11 | From script, which can lay them out instead: | ||
| 12 | |||
| 13 | {{code language="velocity"}} | ||
| 14 | {{velocity}} | ||
| 15 | #foreach ($uix in $services.uix.getExtensions('org.xwiki.platform.panels.Applications')) | ||
| 16 | * $services.rendering.render($uix.execute(), 'xhtml/1.0') | ||
| 17 | #end | ||
| 18 | {{/velocity}} | ||
| 19 | {{/code}} | ||
| 20 | |||
![]() |
6.1 | 21 | The id is a plain string that nothing validates, so a point nobody contributes to and a point whose id is misspelled both render the same nothing. The points a release offers therefore cannot be read off the code, which is why each one gets a documentation page: [[All Bundled UI Extension Points>>doc:documentation.xs.dev.ui-extension-points.bundled.WebHome]] lists the ones XWiki ships, [[UI Extension Points (Extensions)>>doc:documentation.extensions.dev.ui-extension-points.WebHome]] the ones an installed extension brings, and the older [[Available Extension Points>>xwiki:Documentation.DevGuide.ExtensionPoint.WebHome]] list still holds the points neither page covers yet. |
![]() |
2.1 | 22 | |
| 23 | == How a Contribution Reaches the Point == | ||
| 24 | |||
| 25 | {{plantuml}} | ||
| 26 | @startuml | ||
| 27 | !theme bluegray | ||
| 28 | skinparam nodesep 30 | ||
| 29 | skinparam ranksep 40 | ||
| 30 | |||
| 31 | rectangle "The page being rendered\nasks for an extension point id" as P | ||
| 32 | rectangle "UIExtensionManager" as M | ||
| 33 | database "Context component manager:\nthe UIExtension components\nvisible to this user, in this wiki" as CM | ||
| 34 | card "Those whose extension\npoint id matches" as L | ||
| 35 | rectangle "execute() on each of them" as E | ||
| 36 | card "The blocks they returned,\nwhere the point was asked for" as OUT | ||
| 37 | |||
| 38 | P --> M : point id | ||
| 39 | M --> CM : look up | ||
| 40 | CM --> L | ||
| 41 | L --> E | ||
| 42 | E --> OUT | ||
| 43 | @enduml | ||
| 44 | {{/plantuml}} | ||
| 45 | |||
| 46 | The lookup goes through the context component manager, so a contribution registered for the current user or the current wiki is found only there, and the same point rendered on another wiki of the farm does not see it. A component implementing ##UIExtensionManager## and registered under the point's own id replaces that lookup for that one point, which is how a point that computes its contributions rather than collecting them is built. | ||
| 47 | |||
| 48 | == Two Ways to Contribute == | ||
| 49 | |||
| 50 | A contribution is either an ##XWiki.UIExtensionClass## object on a wiki page, which needs no build and no restart, or a Java component implementing the ##UIExtension## role. XWiki turns the object into one of those components, so both arrive at the same place. The object holds: | ||
| 51 | |||
| 52 | |=Field|=What it holds | ||
| 53 | |##extensionPointId##|The id of the point this contribution answers. | ||
| 54 | |##name##|The id of the contribution itself, by which the ##select##, ##exclude## and ##sortByCustomOrder## filters address it. | ||
| 55 | |##content##|The wiki syntax executed when the point is rendered, with the rights of the page's author. | ||
| 56 | |##parameters##|The values the point reads, one ##key=value## per line. | ||
| 57 | |##scope##|Current User, Current Wiki or Global, which decides the component manager the contribution is registered in and therefore who sees it. | ||
| 58 | |||
| 59 | == Order Is the Caller's Decision == | ||
| 60 | |||
| 61 | The manager returns the contributions in no guaranteed order, and the ##uiextensions## macro inserts them as it receives them. A point that needs an order asks for it, by passing the hint of a ##UIExtensionFilter## component and the argument that filter takes: | ||
| 62 | |||
| 63 | {{code language="velocity"}} | ||
| 64 | $services.uix.getExtensions('org.xwiki.platform.panels.Applications', {'sortByParameter': 'order'}) | ||
| 65 | {{/code}} | ||
| 66 | |||
| 67 | The bundled filters are ##sortById##, ##sortByParameter##, ##sortByCustomOrder##, ##select## and ##exclude##. A point that sorts on a parameter says so in its own documentation, since that parameter is part of what it asks its contributions for. |
