|
Note
|
Fork of the MediaWiki extension PageForms, aimed at simplifying contributions by hosting on GitHub instead of Gerrit, improving quality through better test coverage and continuous integration. From time to time, changes in the upstream repository may be cherry-picked and integrated. |
Page Forms, previously known as Semantic Forms, is a MediaWiki extension aimed at simplifying data management through forms. In essence, Page Forms allows users to create, edit, and query data on a wiki without the need for coding. The central feature of Page Forms is the form definition pages, located in the 'Form:' namespace. These pages utilize markup code to define forms, empowering users to create and modify forms without requiring programming knowledge. Forms populate predefined templates and page sections, facilitating data storage and querying, especially when used with Semantic MediaWiki.
For documentation see: https://www.mediawiki.org/wiki/Extension:Page Forms
We use SemVer for versioning. See the tags in this repository for available versions.
We welcome contributions from the community to help make Page Forms better! Whether you’re fixing bugs, adding new features, improving documentation, or anything else, your efforts are valuable.
Click to see detailed Contributing Guidelines
Step 1: Clone the Repository
Fork and clone our repo to your local machine:
git clone https://github.com/username/projectname.git --recursiveStep 2: Ensure test container is running
This repository supports "docker-compose-ci" based CI and testing for MediaWiki extensions.
The docker-compose-ci repository has already been integrated into the Page Forms repository as a Git submodule. It uses "Make" as main entry point and command line interface.
Ensure, you have Make and Docker installed:
make --version
docker --versionRun lint, phpcs and tests:
make ciStep 3: Start coding
Make sure there is an issue that describes your changes. Create a new branch and start working on your changes. In issue-1234 replace 1234 with your issue number:
git checkout -b issue-1234Coding Conventions — General
All source files regardless of language must follow these baseline rules.
They are enforced by make ci (lint + phpcs + eslint).
-
Encoding: UTF-8 without BOM
-
Line endings: Unix-style LF (not CR+LF)
-
Indentation: tabs, not spaces
-
Maximum line length: 120 characters
-
No trailing whitespace
-
Newline at end of file
Coding Conventions — PHP
Tooling: mediawiki-codesniffer via PHPCS.
Run locally: make composer-phpcs (or make ci).
File structure
-
Every file starts with
declare( strict_types=1 ); -
No closing
?>tag -
One class per file; filename matches class name (UpperCamelCase, e.g.
MyClass.php) -
New code belongs in
src/following PSR-4;includes/is legacy and should be migrated incrementally
Namespaces and autoloading
-
PSR-4 via Composer (
autoload.psr-4incomposer.json) -
Top-level namespace = extension name (e.g.
MediaWiki\Extension\FooBar...) -
Acronyms treated as single words:
HtmlId, notHTMLId
Naming
| Element | Convention | Example |
|---|---|---|
Classes, interfaces, traits |
UpperCamelCase |
|
Methods, variables |
lowerCamelCase |
|
Constants |
UPPER_CASE |
|
Global variables |
|
|
Type system
-
Use native type declarations on all parameters, properties, and return types
-
PHPDoc only when native types are insufficient (e.g.
string[],array<string, Foo>) -
Nullable parameters:
?Type, notType $x = null -
Prefer
??(null coalescing) and??=over ternary isset checks -
Use arrow functions
fn( $x ) ⇒ $x * 2for single-expression closures
Modern PHP features (target: PHP 8.1+)
-
Constructor property promotion
-
readonlyproperties for immutable value objects -
enuminstead of class constant groups -
match()instead ofswitchwhen returning a value
Code style
-
1TBS brace style — opening brace on same line,
else/elseifon closing brace line -
Always use braces, even for single-line blocks
-
Spaces inside parentheses:
getFoo( $bar ), empty:getBar() -
Spaces around binary operators:
$a = $b + $c -
Single quotes preferred; double quotes for string interpolation
-
===strict equality;==only when type coercion is intentional -
No Yoda conditions:
$a === 'foo', not'foo' === $a -
elseifnotelse if -
true,false,nullalways lowercase
Architecture
-
privateby default;protectedonly when subclass access is needed -
Dependency injection over direct instantiation — delegate
new Foo()to factories -
Single Responsibility: one class, one concern
-
No superglobals (
$_GET,$_POST) — useWebRequestviaRequestContext -
No new global functions — use static utility classes (
Html,IP) if needed -
Order class members:
public→protected→private
Coding Conventions — JavaScript
Tooling: ESLint with eslint-config-wikimedia.
Run locally: npm run lint:js (or make ci).
ESLint configuration
Every repository must have a .eslintrc.json at root with "root": true:
{
"root": true,
"extends": [
"wikimedia/client/es2016",
"wikimedia/jquery",
"wikimedia/mediawiki"
],
"env": { "commonjs": true }
}Module system
-
CommonJS modules:
require()for imports,module.exportsfor exports -
Register modules with ResourceLoader; bundle name pattern:
ext.myExtension -
JS class files match the class name exactly (
TitleWidget.jsforTitleWidget)
Naming
-
Variables and methods: lowerCamelCase
-
Constructors / classes: UpperCamelCase
-
jQuery objects:
$-prefix ($button, notbutton) -
Constants:
ALL_CAPS -
Acronyms as single words:
getHtmlApiSource, notgetHTMLAPISource
Code style
-
Tabs for indentation; single quotes for string literals
-
===and!==; no Yoda conditions -
Spaces inside parentheses:
if ( foo ),getFoo( bar ) -
constandlet— nevervarin new code -
Arrow functions for callbacks
jQuery
-
Prefer ES6/DOM equivalents over deprecated jQuery methods (
.each→forEach, etc.) -
Never search the full DOM with
$( '#id' )or$( '.selector' ); use hook-provided$contentand call.find()on it -
Prefer
$( '<div>' ).text( value )over$( '<div>text</div>' )to avoid XSS
MediaWiki APIs
-
Access configuration via
mw.config.get( 'wgFoo' ), never direct globals -
Expose public API via
module.exportsor within themwnamespace (e.g.mw.echo.Foo) -
Use
mw.storage/mw.storage.sessionfor localStorage/sessionStorage -
Storage keys:
mw-prefix + camelCase/hyphens (e.g.mwedit-state-foo)
Coding Conventions — CSS / LESS
Tooling: stylelint via npm run lint:styles (or make ci).
ResourceLoader natively compiles .less files; prefer LESS over plain CSS.
Naming
-
Classes and IDs: all-lowercase, hyphen-separated
-
Use an extension-specific prefix to avoid conflicts (e.g.
pf-,smw-,mw-) -
LESS mixin names:
mixin-prefix + hyphen-case (e.g.mixin-screen-reader-text)
Whitespace and formatting
-
One selector per line, one property per line
-
Opening brace on the same line as the last selector
-
Tab indentation for properties and nested rules
-
Semicolon after every declaration, including the last
-
Empty line between rule sets
Colors
-
Lowercase hex shorthand preferred:
#fff,#252525 -
rgba()when alpha transparency is needed;transparentkeyword otherwise -
No named color keywords (except
transparent), norgb(),hsl(),hsla() -
Ensure color contrast meets WCAG 2.0 AA
LESS specifics
-
CSS custom properties (design tokens) preferred over LESS variables for new code
-
@importonly for mixins and variables (variables.less,mixins.less); do not use@importfor bundling conceptually related files -
Omit
.lessextension in@importstatements -
Bundle related files via the
stylesarray inskin.json/extension.json
Anti-patterns to avoid
-
!important— avoid except when overriding upstream code that also uses it -
z-index— use natural DOM stacking order where possible; document exceptions -
Inline
styleattributes — always use stylesheet classes instead -
float/text-align: lefthardcoded — use/* @noflip */annotation when needed, otherwise ResourceLoader’s CSSJanus handles RTL automatically
Step 4: Test your changes
Before making any code changes to fix a bug or implement a feature:
-
Check whether an existing test already covers the described behavior.
-
If not, write or adapt a test that reproduces the issue — it must fail first.
-
Only after a failing test exists, make the code changes.
-
Re-run the test to confirm it passes (green).
Test-first approach
All tests run inside a containerized MediaWiki environment managed via
docker-compose-ci (the build/ submodule).
Never run tests directly against a local PHP or Node.js installation.
Always run make install before executing tests to ensure that the latest file
changes are copied into the container. Changes to source or test files on the
host are not automatically reflected in a running container.
make installPHPUnit tests
Run all PHPUnit tests:
make install composer-phpunitRun a single test class or method (filtered):
make install composer-phpunit COMPOSER_PARAMS="-- --filter YourTestName"Run a specific test suite:
make install composer-phpunit COMPOSER_PARAMS="-- --testsuite your-suite-name"For interactive use, bash into the running container:
make bash
> composer phpunit -- --filter YourTestNameNode QUnit tests
Run all JavaScript tests:
make install npm-testThere is no direct make target for filtering individual tests.
Bash into the running container to run a specific test file or test case:
make bash
> npm run node-qunit -- tests/node-qunit/yourtest.test.jsFilter by test description:
make bash
> npx qunit --require ./tests/node-qunit/setup.js 'tests/node-qunit/**/*.test.js' --filter "your test description"Pre-commit validation gate
Before every commit, run the full CI suite to confirm nothing is broken:
make ciStep 5: Commit your changes
Commit messages follow the Conventional Commits specification.
Commit format:
type(scope): short description
The scope is optional and should describe the affected subsystem, module, or dependency when useful.
Examples:
-
feat(api): add autocomplete endpoint
-
fix(parser): handle empty token lists
-
docs(readme): explain input architecture
-
refactor(parser): simplify token parsing
-
deps(smw): bump from 5.1.0 to 5.2.0
-
ci(github): update workflow configuration
-
test(api): add autocomplete tests
Recommended commit types:
-
feat— new functionality -
fix— bug fixes -
deps— dependency updates -
docs— documentation changes -
refactor— internal code changes without behavioral change -
test— tests added or updated -
ci— changes to continuous integration configuration -
chore— repository maintenance tasks without impact on runtime behavior
Dependency updates:
-
Use the
depstype for dependency upgrades -
The scope should identify the dependency being updated
-
Include the version change when applicable
Example:
-
deps(smw): bump from 5.1.0 to 5.2.0
Guidelines:
-
Use the imperative mood (e.g. "add feature", not "added feature")
-
Keep the subject line concise
-
Use the commit body to explain why, not only what
-
Scopes should be short, lowercase identifiers (e.g.
api,parser,smw,mediawiki,docker) -
Use
choreonly for repository maintenance tasks that do not affect runtime behavior, dependencies, CI configuration, or tests
git commit -m "Description of your changes"Step 6: Push!
Push your branch and open a pull request.
git push origin feature/your-feature-nameFor focused test runs (for example autocomplete-related API tests), use:
make install composer-phpunit COMPOSER_PARAMS="-- --filter=autocomplete"After the first run, you can use this faster variant for repeated runs:
make composer-phpunit COMPOSER_PARAMS="-- --filter=autocomplete"Run the default JS test pipeline (ESLint + i18n + Node QUnit):
make npm-testRun only Node QUnit tests (faster feedback loop):
docker compose -f build/docker-compose.yml --project-name pageforms-mysql --profile mysql exec -T wiki bash -lc "cd /var/www/html/extensions/PageForms && ./node_modules/.bin/qunit --require ./tests/node-qunit/setup.js 'tests/node-qunit/**/*.test.js'"Run one specific JS test file:
docker compose -f build/docker-compose.yml --project-name pageforms-mysql --profile mysql exec -T wiki bash -lc "cd /var/www/html/extensions/PageForms && ./node_modules/.bin/qunit --require ./tests/node-qunit/setup.js tests/node-qunit/simpleupload.test.js"New or renamed test files are copied into the Docker image at build time.
After changing test files, run commands via make install … at least once,
then you can use make composer-phpunit …, make npm-test, or direct
qunit runs in the container for repeated runs.
This extension is part of semantic::core - Enterprise Class MediaWiki distribution.
.