Attachment Picker Module

Last modified by Eleni Cojocariu on 2026/10/01 09:49

Reference

The RequireJS module xwiki-attachment-picker, one of the two modules of the Attachment Picker WebJar, turns markup into a working picker: it builds the search field, fills the grid of results, and reports what the reader picks.

Markup of a Picker

A picker is an element carrying the class attachmentGalleryPicker, holding four blocks that the JavaScript looks up inside it by class:

Block classHolds
attachmentPickerSearchThe search field, which the JavaScript builds into it, and the class xform, which it adds. The field is a Bootstrap input-group: a text input and a pair of radio buttons named scope, of values local and global, with local checked.
attachmentPickerResultsThe grid of results, filled and emptied at every search.
attachmentPickerNoResultsThe message shown when a search returns nothing. Rendered with the class hidden, which the JavaScript removes and adds back.
attachmentPickerGlobalSelectionThe warning shown while the selected attachment belongs to another Page. Rendered hidden too.

Three optional attributes on the root element configure it. The JavaScript only reads them, and they are the three parameters of the attachmentGalleryPicker macro:

AttributePassed to SolrSearch asDefault
data-xwiki-attachment-picker-limitlimit, parsed as an integer20, both for an absent attribute and for a value that does not parse
data-xwiki-attachment-picker-targettargetThe Page the picker is displayed on
data-xwiki-attachment-picker-filterfilter of the Solr options, where it becomes a media type clause of every queryNo filter

The macro writes exactly that markup, but any markup carrying those classes works, once the module is loaded on the Page as described below:

<div class="attachmentGalleryPicker"
     data-xwiki-attachment-picker-filter="image/*"
     data-xwiki-attachment-picker-limit="10"
     data-xwiki-attachment-picker-target="Sandbox.WebHome">
  <div class="attachmentPickerSearch"></div>
  <div class="attachmentPickerResults"></div>
  <div class="attachmentPickerNoResults hidden">No attachment matches your search.</div>
  <div class="attachmentPickerGlobalSelection hidden">The attachment you picked belongs to another
    page.</div>
</div>

Initialisation

The module registers the jQuery plugin $.fn.attachmentGalleryPicker():

require(['jquery', 'xwiki-attachment-picker'], function ($) {
  // The module exports nothing: requiring it is what registers the plugin.
  $('#my-picker').attachmentGalleryPicker();
});
TriggerWhat happens
A call of the pluginOne picker is built and initialised per matched element.
The document being ready, then every xwiki:dom:updated eventThe module initialises every element of class attachmentGalleryPicker by itself, among the elements the event carries or in the whole document, so a picker inserted by an asynchronous request needs no call of your own.
Initialising a pickerIt is marked with the class initialized, and a first search runs with an empty query, so the grid is filled before the reader types anything.
Initialising a picker that already carries initializedNothing, which is what makes the automatic initialisation safe.
Typing in the search field, or switching scopeA new search, debounced by 500 milliseconds on both.

Results

Every result of a search is one attachmentGroup element:

<span class="attachmentGroup localAttachment">   <!-- jQuery data "id": the attachment reference -->
  <a title="logo.png" href="/xwiki/bin/download/Sandbox/WebHome/logo.png">
    <span class="previewWrapper">
      <img loading="lazy" alt="logo.png"
           src="/xwiki/bin/download/Sandbox/WebHome/logo.png?width=150&height=150&keepAspectRatio=true">
    </span>
    <span class="attachmentTitle" title="logo.png">logo.png</span>
  </a>
</span>
Carried by a resultValue
The class localAttachment or globalAttachmentWhether the attachment comes from the Page being searched or from another one
The class selectedSet on the selected result
The jQuery data id, on the groupThe reference of the attachment, held as data rather than as an attribute
The jQuery data index, on the linkThe rank of the result in the grid, held the same way
The attachmentTitle span, and the title of both the span and the linkThe file name
PreviewShown for
The attachment itself, in an img whose address carries ?width=150&height=150&keepAspectRatio=true, so the wiki resizes it into a 150 by 150 box while preserving its aspect ratioA media type starting with image/
A span carrying the class of the media type's icon, taken from xwiki-attachments-icon, plus the class attachmentIconAny other media type, with a font icon theme
An image element pointing at the address of that iconAny other media type, with a theme that is not a font one

While a search runs, the results block is emptied and carries the class loading. A search that fails is logged to the browser console and reported to the reader as an error notification.

Selection

A picker reports what the reader picks through two events fired on its root element, documented with an example on the attachmentGalleryPicker macro page.

RuleBehaviour
Selection is singleSelecting a result unselects every other.
Clicking the selected resultClears the selection.
Double clickOnly the first click of a chain selects, so the second click does not undo the selection the first one made.
A new searchKeeps the selection while the attachment is still among the results, and clears it otherwise.
The attachmentPickerGlobalSelection blockShown exactly while the selected result carries globalAttachment, and hidden again when the selection is cleared or a new search runs.

Messages

The four messages of the picker are loaded through xwiki-l10n!, under the prefix attachment.picker., and none of them is defined in this WebJar:

KeyLabelsDefined by
solrSearch.query.errorMessageThe notification shown when a search failsThe attachmentGalleryPicker macro module
searchField.placeholderThe search fieldThe Attachment.Picker.Code.Translations page of the Attachment Picker Application
searchField.scope.currentPageThe scope button of the current PageThe same page
searchField.scope.allPagesThe scope button of every PageThe same page

The texts of the two blocks the JavaScript only shows and hides, attachment.picker.macro.notResult.message and attachment.picker.macro.globalSelection.message, belong to the macro module as well.

FAQ

Which of the four blocks may I leave out?

Any of them. Each one is looked up inside the root element, and a block that is not there is quietly skipped instead of raising an error, which costs the picker the feature that block carries: no search field, or a reader who is never told that a search found nothing or that the attachment they picked belongs to another Page.

Related

Get Connected