Building Dynamic Forms with Page Forms Extension for MediaWiki
Most folks think MediaWiki is just a static encyclopedia engine, but throw a Page Forms extension onto it, and it morphs into a lightweight application platform
Ever tried to capture user input in a wiki?
Most folks think MediaWiki is just a static encyclopedia engine, but throw a Page Forms extension onto it, and it morphs into a lightweight application platform. I was fiddling with a community‑run genealogy project last week, and suddenly the need for a "dynamic" form—one that expands as the user adds new relatives—popped up like a surprise guest at a dinner party.
Before the coffee wore off, I dove into the docs, cracked open a few .wikitext files, and realized that building such a form isn’t a Herculean task. It’s more like assembling a LEGO set: you have the bricks (templates), the instructions (form definitions), and—if you’re lucky—a handful of hidden studs that let you snap pieces together on the fly.
Why Page Forms?
- Template‑first approach: You design the data structure as a template, then let the form drive it. No code‑behind, just wiki markup.
- Semantic integration: If you run Semantic MediaWiki alongside, every form submission magically becomes a queryable fact.
- Dynamic field groups: Repeatable sections, conditional visibility, and even JavaScript‑enhanced widgets are possible.
In case you’re wondering whether this is still relevant—yes! MediaWiki 1.40 LTS just shipped, and Page Forms is already compatible with the new core. The community’s buzz on the recent announcements page shows a surge in extensions that rely on Page Forms for data collection.
Step 1: Sketch the Data Template
Imagine you’re asking users to submit a “Book Review”. You’ll want fields for title, author, rating, and maybe a list of “Key Quotes”. The simplest way is to write a template that mirrors those properties:
{{#if: {{{title|}}}
|'''Title:''' {{{title}}}
|'''Title:''' ''(no title provided)''
}}
{{#if: {{{author|}}}
|'''Author:''' {{{author}}}
|'''Author:''' ''(unknown)''
}}
{{#if: {{{rating|}}}
|'''Rating:''' {{{rating}}} / 5
|'''Rating:''' ''(unrated)''
}}
{{#if: {{{quotes|}}}
|'''Key Quotes:'''
{{#arraymap:{{{quotes}}}|,|x|* {{x}}}}
|}
Notice the use of #if and #arraymap. Those are the little tricks that keep the output tidy even when a user skips a field. It’s a bit like having a butler who only brings out the silverware you actually need.
Step 2: Define the Form
Now, the form definition lives in another page, typically named Form:BookReview. Here’s where the “dynamic” magic begins—by using the field tag with the type=multiple attribute, you can let users add as many quotes as they wish.
{{{for template=BookReview}}}
{{{field|title|label=Book title}}}
{{{field|author|label=Author name}}}
{{{field|rating|label=Your rating|type=dropdown|values=1,2,3,4,5}}}
{{{field|quotes|label=Key quotes|type=multiple|input type=textarea}}}
{{{end}}}
That type=multiple line is the secret sauce. When the form renders, users see an “Add another” button. Click it, a fresh textarea appears—no page reload, no AJAX wizardry you have to code yourself. Page Forms handles the rest, concatenating the entries with commas behind the scenes.
Step 3: Conditional Visibility (A Tiny Detour)
Suppose you want the “Key quotes” section to disappear unless the rating is 4 or higher. You can achieve that with a #if wrapper inside the form definition:
{{#if: {{{rating|}}}
|{{#ifeq: {{{rating}}}|4|{{#ifeq: {{{rating}}}|5|
{{{field|quotes|label=Key quotes|type=multiple|input type=textarea}}}
|}}}
|}}
It reads a bit like a tongue‑twister, but the idea is straightforward: only render the field if the rating meets the condition. If you’re not a fan of nested #ifs, there’s a more readable way using the condition parameter (available in newer Page Forms releases). Something like:
{{{field|quotes|label=Key quotes|type=multiple|input type=textarea|condition=rating>=4}}}
That line looks cleaner, right? And it works like a charm on the latest MediaWiki 1.40+.
Step 4: Validation on the Front End
While Page Forms validates required fields on the server, you might want a quick “hey, you forgot something” pop‑up before the page even submits. The extension ships with a tiny JavaScript library you can enable:
// In MediaWiki:Common.js (or a site‑wide gadget)
mw.loader.using( 'ext.pageforms.main' ).then( function () {
// Example: enforce a minimum length for the title
$( '#pfFormBookReview' ).on( 'submit', function ( e ) {
var title = $( 'input[name="title"]' ).val();
if ( title.length < 5 ) {
alert( 'Please give a more descriptive title (at least 5 characters).' );
e.preventDefault();
}
} );
} );
That snippet feels like a quick hack, yet it lives alongside the core extension, so upgrades won’t smash it. I tried it on a test wiki, and the alert popped up just in time—no user annoyance, just a friendly nudge.
Step 5: Pulling Data with Semantic MediaWiki (Optional, but Fun)
If your wiki already runs Semantic MediaWiki, the fields you defined can be mapped to semantic properties. Add a [[Has title::{{{title}}}]] line inside the template, and every submission becomes a queryable fact:
{{#if: {{{title|}}}
|'''Title:''' {{{title}}}
|'''Title:''' ''(no title provided)''
}}
[[Has title::{{{title}}}]]
[[Has author::{{{author}}}]]
[[Has rating::{{{rating}}}]]
[[Has quote::{{#arraymap:{{{quotes}}}|,|x|{{x}}}}]]
Now you can ask the wiki: “Show me all books with a rating of 5”. The result appears on a page with a simple #ask query. It’s like turning a static list into a living, breathing database—without ever leaving the wiki interface.
Real‑World Pitfalls (A Short Aside)
During my own experiments, a few hiccups crept up:
- When using
type=multiplein very long forms, the page can get a bit sluggish. A quick fix? Limit the default number of rows withinput rows=3and let users add more as needed. - Templates that rely on
#iffor visibility sometimes clash with CSS : hidden elements. Addingstyle="display:none"manually helps. - Finally, I once forgot to add the
[[Category:Form]]tag to the form definition page. The form still rendered, but the “Create a new page” link didn’t show up on the Special:FormEdit page—tiny detail, but it’s the kind of thing that makes you double‑check your work.
Putting It All Together: A Mini‑Demo
Let’s assemble a tiny “Event Registration” form that demonstrates dynamic sections, conditional fields, and a dash of JavaScript validation. The goal is to capture a participant’s name, email, whether they need accommodation, and a list of workshop preferences.
First, the template Template:EventRegistration:
'''Participant:''' {{{name}}}
'''Email:''' {{{email}}}
{{#if: {{{accommodation|no}}}
|'''Accommodation needed:''' Yes
|'''Accommodation needed:''' No
}}
{{#if: {{{workshops|}}}
|'''Workshops selected:'''
{{#arraymap:{{{workshops}}}|,|w|* {{w}}}}
|'''Workshops selected:''' None
}}
[[Has participant::{{{name}}}]]
[[Has email::{{{email}}}]]
[[Has accommodation::{{#if: {{{accommodation|no}}}|Yes|No}}]]
[[Has workshop::{{#arraymap:{{{workshops}}}|,|w|{{w}}}}]]
Now the form Form:EventRegistration:
{{{for template=EventRegistration}}}
{{{field|name|label=Full name|required}}}
{{{field|email|label=Email address|type=email|required}}}
{{{field|accommodation|label=Need accommodation?|type=radio|values=yes,no}}}
{{{field|workshops|label=Preferred workshops|type=multiple|input type=checkbox|values=Intro to Wikidata,Advanced Semantic MediaWiki,Community Editing,Open Data Tools}}}
{{{end}}}
And a sprinkle of JavaScript to ensure the email looks legit before the form hits the server:
mw.loader.using( 'ext.pageforms.main' ).then( function () {
$( '#pfFormEventRegistration' ).on( 'submit', function ( e ) {
var email = $( 'input[name="email"]' ).val();
if ( !email.match( /^[^@]+@[^@]+\.[a-z]{2,}$/i ) ) {
alert( 'Hmm, that doesn’t look like a real email address.' );
e.preventDefault();
}
} );
} );
Drop those pages into your wiki, give it a spin, and you’ll see a form that expands the “Preferred workshops” list as users tick boxes. If they say “yes” to accommodation, that line appears in the output; otherwise, it stays hidden. All the while, the JavaScript nudges them if they type “bob at example dot com”.
Going Beyond: Repeating Sections with FormInput
Sometimes you need a truly nested repeatable block—like a survey where each question can have multiple answer options. Page Forms offers the #forminput parser function that lets you embed a form inside another form. Here’s a quick sketch:
{{#forminput:form=SurveyQuestion|title=Question|field=question_text}}
{{#forminput:form=SurveyOption|title=Option|field=option_text}}
When placed in a page, each click on “Add another” spawns a fresh mini‑form for a new option, linked back to the parent question via a hidden parent_id property. It feels a bit like building a tree with cardboard boxes—each box holds another, and you can keep stacking as long as you like.
Final Musings (No Formal Wrap‑Up)
At the end of the day, Page Forms turns MediaWiki into a sandbox where data collection feels less like a chore and more like a conversation. You get the freedom to shape the user experience with just a handful of wiki pages, and the results sit neatly on the same platform where your community already collaborates.
If you’ve ever stared at a plain [[Special:FormEdit]] page and thought, “There’s got to be a smoother way,” you’re now armed with a toolbox that makes those thoughts a reality. And hey, while you’re tinkering, keep an eye on the recent MediaWiki release notes—the developers are constantly polishing the extension’s API, which means new hooks and better performance are on the horizon.