DomainPunch Logo

 A Service of Softnik Technologies

WCAG Accessibility Tools for Watch My Domains SED

Custom Tools icon Custom Tools icon This is a custom tool available in addition to the standard Watch My Domains SED interface. To access custom tools, look for the custom tools icon in the main toolbar. These custom tools will be available in versions released after March/April 2026.

A native WMD SED extension that scans domains for WCAG accessibility issues using Puppeteer and axe-core, stores summary results in custom domain columns, and provides a browser-based report tool.


Overview

This extension consists of two parts with different deployment paths.

Runtime tools ship as part of the WMD SED application in tools/wcag/:

Setup script is downloaded separately and run once by the administrator:


Standard Installation Layout

For a standard WMD SED installation created by the WMD SED install script:

/home/wmdsed/
    sites/
        wmdsed60/                       <- WMD SED root
            tools/
                wcag/
                    axe-scan.js
                    wcag-gather.php
                    wcag-report.php

    webdata/                            <- real Node/npm folder
        node_modules/                   <- Puppeteer + @axe-core/puppeteer
        websites.js                     <- existing screenshot script (untouched)
        axe-scan.js                     <- copied here by wcag-setup.sh

    logs/
        wmdsed/
            websites -> /home/wmdsed/webdata   <- symlink (used by WMD SED)
            wcag-data/                          <- created on first scan run
                example.com.json
                anotherdomain.com.json

The websites folder under the log path is a symlink to /home/wmdsed/webdata/. The setup script resolves this automatically.


Requirements


Setup

Step 1 - Download and run wcag-setup.sh

Download from learn.domainpunch.com/static/downloads/wcag-setup.sh and run as the wmdsed user:

bash wcag-setup.sh

For a standard installation no editing is required. The script defaults to:

For non-standard installations, edit the variables at the top of the script before running. Run with --dry-run first to verify what will happen without making any changes:

bash wcag-setup.sh --dry-run

The script will:

  1. Run pre-flight checks on all required paths and report any problems
  2. Install @axe-core/puppeteer into the Node folder if not already present
  3. Copy axe-scan.js from tools/wcag/ to the Node folder if not already present
  4. Add a cron job for automatic scanning (configurable via INSTALL_CRON at the top of the script)

Step 2 - Create custom WMD columns (one time only)

php /home/wmdsed/sites/wmdsed60/tools/wcag/wcag-gather.php --setup-columns

This creates four custom columns in WMD SED:

ColumnTypeContent
wcag_statusstringpass / fail / partial / error
wcag_levelstringA / AA / AAA / none
wcag_issue_countstextJSON e.g. {"critical":2,"serious":5,"moderate":1,"minor":3}
wcag_scanned_atdatetime2026-03-26 14:32:00

Safe to run multiple times - skips columns that already exist.

Step 3 - Create the WCAG category

In WMD SED, create a category named WCAG Test Domains and assign the domains you want to scan to it. This is the default category used by wcag-gather.php. The name can be changed via the WCAG_DEFAULT_CATEGORY constant at the top of wcag-gather.php.

Step 4 - Dry run to verify domain selection

php /home/wmdsed/sites/wmdsed60/tools/wcag/wcag-gather.php --dry-run

Lists every domain that would be scanned without actually scanning anything.

Step 5 - Run the first scan

php /home/wmdsed/sites/wmdsed60/tools/wcag/wcag-gather.php
php /home/wmdsed/sites/wmdsed60/tools/wcag/wcag-gather.php --batch=3

Configuration

All constants are at the top of wcag-gather.php:

ConstantDefaultPurpose
WCAG_NODE_FOLDERwebsitesSubfolder name under log path where Node scripts live
WCAG_OUTPUT_FOLDERwcag-dataSubfolder name under log path where JSON output is stored
WCAG_DEFAULT_CATEGORYWCAG Test DomainsWMD category name to scan by default
WCAG_PROTOCOL_COLUMN''Custom column holding protocol per domain, or empty for auto
WCAG_SKIP_RECENT_DAYS7Skip domains scanned within this many days
WCAG_LEVELAAWCAG level to test against: A, AA, or AAA
WCAG_TIMEOUT_MS30000Puppeteer navigation timeout in milliseconds
WCAG_LOCK_FILEwcag-gather.lockLock file name under log path

WCAG_OUTPUT_FOLDER must match the constant of the same name in wcag-report.php.

Protocol resolution

If WCAG_PROTOCOL_COLUMN is set to a custom column name (e.g. site_protocol), the scanner reads the protocol from that column for each domain. If empty, the scanner tries https:// first and falls back to http:// automatically on failure.


CLI Reference - wcag-gather.php

ArgumentTypeDescription
--category="Name"key=valueOverride default category. Case-sensitive.
--domain=example.comkey=valueScan one domain only. Ignores skip-recent rule.
--batch=3key=valueRun N Puppeteer processes in parallel (default: 1).
--skip-recent=14key=valueOverride WCAG_SKIP_RECENT_DAYS for this run.
--forcebare flagRe-scan all matching domains regardless of wcag_scanned_at.
--setup-columnsbare flagCreate the four wcag_* custom columns and exit.
--dry-runbare flagShow what would be scanned. No scans, no DB writes.
--debugbare flagVerbose output including SQL, params, and raw results.

Re-scan behaviour

ScenarioBehaviour
wcag_scanned_at is emptyAlways scanned
Scanned within WCAG_SKIP_RECENT_DAYSSkipped
--force flag presentAlways scanned
--domain=x specifiedAlways scanned (force implied)

Domain eligibility

A domain is excluded from scanning if all three of the following are true:

Domains with availability = 'Not Available' or 'possibly available' are always included.

Concurrency lock

wcag-gather.php uses a lock file (wcag-gather.lock in the log folder) to prevent concurrent runs. If a second instance is started while one is already running, it exits immediately with a warning. Stale locks from crashed processes are detected via PID check and removed automatically.


What axe-scan.js Does

Called once per domain by wcag-gather.php via shell_exec() or proc_open(). Executed from the Node folder so it resolves node_modules correctly.

  1. Launches headless Chromium via Puppeteer
  2. Navigates to the domain URL - tries https:// first, falls back to http:// on failure
  3. Runs axe-core at the configured WCAG level
  4. Outputs a single JSON object to stdout - always valid JSON even on error

Scan result status values

StatusMeaning
passNo violations found at the tested WCAG level
failOne or more violations found
partialNo violations but some items could not be fully evaluated
errorNavigation failed - site unreachable or timed out

wcagLevel reflects the highest WCAG level at which the page fully passes. A page with no AA violations but some AAA violations returns AA. Full violation JSON files are saved to [log folder]/wcag-data/[domain].json and loaded on demand by the browser report popup.


Browser Report - wcag-report.php

Access as an authenticated WMD administrator via the Custom Built-in Tools page, or directly at https://yourserver/tools/wcag-report.php.

Features

If the WCAG columns have not been created yet, the report page shows a setup guide with the exact commands to run.


Cron Job

The wcag-setup.sh script automatically adds a cron job when INSTALL_CRON="true" (the default). The default schedule scans every 30 minutes with a batch size of 3. Both settings are configurable at the top of wcag-setup.sh before running.

The lock file prevents re-entry if a previous run is still in progress, so overlapping cron runs are not a concern.

To add or edit the cron job manually:

crontab -e

Add a line in the form:

*/30 * * * * php /home/wmdsed/sites/wmdsed60/tools/wcag/wcag-gather.php --batch=3 >> /home/wmdsed/logs/wmdsed/wcag-cron.log 2>&1

Security Notes

Close