Wiki source code of Skin Extensions
Last modified by superadmin on 2026/10/14 18:45
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | The [[Skin Extension Plugin>>doc:extensions:Extension.Skin.Skin Extension Plugin]] lets an interface component pull the CSS and the JavaScript it needs, so a page or a whole wiki changes its look and its behaviour without anybody editing the skin's templates or stylesheets. The artifact is ##xwiki-platform-skin-skinx##, whose sources are in {{scm path="xwiki-platform-core/xwiki-platform-skin/xwiki-platform-skin-skinx"/}}. | ||
| 2 | |||
| 3 | An extension is an [[XWiki object>>doc:documentation.xs.dev.data-model.x-objects.WebHome]] on a wiki page, of class ##XWiki.JavaScriptExtension## (JSX) or ##XWiki.StyleSheetExtension## (SSX). Being an object, it is written from the browser, with no access to the filesystem and no restart. SSX works exactly like JSX, the only difference being that the code is CSS. [[Create a Skin Extension>>doc:documentation.xs.dev.skin-extensions.create.WebHome]] is the procedure, and [[Skin Extension Object Fields>>doc:documentation.xs.dev.skin-extensions.object-fields.WebHome]] says what each field of the two classes decides. | ||
| 4 | |||
| 5 | Those two classes are the wiki page form of the mechanism. The plugin ships seven pull hints in all, and four of them take their code from a file of the current skin or from a resource inside a JAR instead of from a page. [[Skin Extension Script API>>doc:documentation.xs.dev.skin-extensions.script-api.WebHome]] lists them with the script call each one answers to. | ||
| 6 | |||
| 7 | An extension is pulled on the pages that need it, from a wiki page or from a Velocity template, or set to load on every page of the wiki. A pull reaches the reader in two round trips: | ||
| 8 | |||
| 9 | {{plantuml}} | ||
| 10 | @startuml | ||
| 11 | !theme bluegray | ||
| 12 | |||
| 13 | participant "wiki page or\nVelocity template" as Caller | ||
| 14 | participant "skinx plugin" as Plugin | ||
| 15 | participant "the response" as Html | ||
| 16 | participant "the browser" as Browser | ||
| 17 | participant "the jsx or ssx action" as Action | ||
| 18 | |||
| 19 | Caller -> Plugin : $xwiki.jsx.use('My.Page') | ||
| 20 | Plugin -> Plugin : records the pull\nfor this request | ||
| 21 | Plugin -> Html : at the end of parsing, replaces the first\nHTML comment named after the plugin class\nwith the generated script tag | ||
| 22 | Html -> Browser : the page, script tag included | ||
| 23 | Browser -> Action : GET /bin/jsx/My/Page | ||
| 24 | Action -> Action : reads the objects of the page,\nparses Velocity or compiles LESS,\nminifies, sets the cache headers | ||
| 25 | Action -> Browser : the extension | ||
| 26 | @enduml | ||
| 27 | {{/plantuml}} | ||
| 28 | |||
| 29 | The comment the plugin writes over is the fully qualified name of the plugin class, {{code language="none"}}<!-- com.xpn.xwiki.plugin.skinx.JsSkinExtensionPlugin -->{{/code}}, and the skin's templates are what leave it in the response: the {{code language="none"}}#javaScriptExtensionHooks{{/code}} and {{code language="none"}}#styleSheetExtensionHooks{{/code}} macros of ##macros.vm## emit one comment per plugin class. Their order there is deliberate, resource then skin file then object, so that a later style sheet overrides an earlier one. Only the first occurrence is replaced, and the plugin does not check that it found one, which is why an extension that never arrives says nothing in the log. Read the page source and look for the comment before suspecting the object. | ||
| 30 | |||
| 31 | A page holds as many objects of a class as it needs, and the response is the concatenation of all of them in document order, one newline apart. The document is therefore the extension, not a container of extensions: several objects are parts of one extension, never two independent ones. The scope follows the same rule. Since the whole document is served as a single response, an object set to load on every page drags every other object of its class on that page into every page of the wiki, so extensions whose scopes differ belong on separate pages. | ||
| 32 | |||
| 33 | The code of an extension does not have to be written by hand. [[Generate Skin Extension Content with Velocity>>doc:documentation.xs.dev.skin-extensions.velocity.WebHome]] covers the extensions the server computes per request, and [[Write a Style Sheet Extension in LESS>>doc:documentation.xs.dev.skin-extensions.less.WebHome]] the style sheets compiled against the variables of the current skin. | ||
| 34 | |||
| 35 | The [[Skin>>doc:documentation.xs.dev.skin.WebHome]] module is a different mechanism. It is the Java API that finds a file inside a skin and follows the skin's inheritance, and it has no part in contributing code to a response. |