Client-Side Attachment Validation

Last modified by Eleni Cojocariu on 2026/10/05 17:09

Reference

The xwiki:actions:beforeUpload event is fired in the browser once per file the reader has picked, before anything is sent to the server. A listener that calls event.preventDefault() turns that one file down. The Attachment Validation Application listens to it twice, for the maximum attachment size and for the allowed and blocked mimetypes, and an extension adds a check of its own the same way.

Where the Event Is Fired

UploadFiredPayload
The "Attachments" tab of a Page, and every other HTML5 upload fieldOnce per file of the selectionfile
The attachmentSelector macroOnce per file field, as the form is submittedfile
The image dialog of the WYSIWYG editorOnce, as "Upload" is selectedfile, documentReference

Payload

KeyValueSet by
fileThe File about to be uploaded. The shipped checks read its name, size and typeEvery upload above
documentReferenceThe reference of the Page the attachment will be written to, serialized. It is what lets a check read the configuration of that Page rather than of the Page the editor is open onThe image dialog of the WYSIWYG editor only

The payload reaches a jQuery listener as its second argument. Two of the three uploads fire the event through Prototype, so a listener that does not require xwiki-events-bridge never runs for them, and sees only the WYSIWYG image dialog:

require(['jquery', 'xwiki-events-bridge'], function ($) {
  $(document).on('xwiki:actions:beforeUpload', function (event, data) {
    if (data.file.name.toLowerCase().endsWith('.exe')) {
      new XWiki.widgets.Notification('Executables cannot be attached.', 'error');
      event.preventDefault();
    }
  });
});

Rules of a Listener

RuleBehaviour
Cancelling silentlyThe file leaves the selection with nothing said. Report the reason first, as the shipped checks do.
Cancelling lateThe event is read as soon as the listener returns, so an asynchronous check, one reading the file with a FileReader for instance, cannot turn the upload down.
Several listenersEvery one of them runs, and a single preventDefault() is enough to refuse the file.
ThrowingThe HTML5 upload field catches it, reports it in place of that file and carries on with the rest of the selection. The two other uploads do not catch it.
What the check is worthNothing is enforced in the browser. The server validates the attachment again, so an upload that never runs this JavaScript, a REST client for one, is refused all the same.

FAQ

Where does the JavaScript go?

In a JavaScript Skin Extension used on all Pages, which is how the shipped size and mimetype checks are themselves written.

Related

Get Connected