WordPress theme powering softcatala.org, the website of Softcatalà — a non-profit organization promoting the Catalan language in technology.
License: GPLv3 or later
- PHP >= 8.2 — production runs 8.4. The floor comes from Timber v2.
- WordPress (recent versions)
- Composer — Timber v2 is a Composer dependency of the theme, not the wordpress.org plugin
- Advanced Custom Fields — a hard dependency: theme hooks call
get_field()unconditionally, and field groups are checked in underacf-json/
composer installThe theme must live in your WordPress wp-content/themes/ directory.
To run the tests you need none of the above — only Docker. See below.
Everything runs inside Docker. No PHP, Composer, Node or npm on the host.
./bin/test.sh # everything CI runs: frontend, lint, PHP testsIndividual checks:
./bin/test.sh phpunit # WordPress test suite
./bin/test.sh frontend # vitest + tsc --noEmit
./bin/test.sh build # production frontend build into static/
./bin/test.sh lint # phpcs + phpmd over all of classes/
./bin/test.sh lint-changed # ...only over files changed vs master
./bin/test.sh phpcbf # auto-fix coding standard violations
./bin/test.sh shell # bash inside the PHP container
./bin/test.sh down # stop containers and drop the cached volumesArguments after phpunit, phpcs and phpcbf are passed straight through:
./bin/test.sh phpunit --filter TascaTest
./bin/test.sh phpunit tests/test-tasca.php
./bin/test.sh phpcs classes/providers/tasques.phpEnvironment overrides:
PHP_VERSION=8.2 ./bin/test.sh phpunit # defaults to 8.4, as in production
WP_VERSION=6.7 ./bin/test.sh phpunit # defaults to the latest releaseThe first run builds the PHP image and downloads WordPress, the wordpress-develop
test suite and ACF into named volumes; later runs reuse them and take seconds.
./bin/test.sh down clears that cache if it ever goes stale.
Composer dependencies are installed into vendor/ in the checkout by the
container, and are shared across PHP_VERSION values locally — CI resolves them
per version instead. composer.lock is resolved against PHP 8.2 (via
config.platform in composer.json) so that one lock installs on every
supported version, 8.4 included.
Tests live in tests/ with the test- prefix and extend SCTests
(tests/sc_tests.php). Two things are worth knowing before writing one:
SCTests::set_up()re-firesinit, becauseWP_UnitTestCaseunregisters post types, taxonomies and meta keys between tests — without it everything the theme registers would be missing for every test after the first.tests/bootstrap.phpactivates the theme before WordPress loads theme functions, sofunctions.phpruns through the normal path and itsinithooks land in time. It also loads ACF.
bin/install-wp-tests.sh provisions WordPress, the test suite and ACF. It needs
neither svn nor a MySQL client, and bin/test.sh calls it for you.
PHPCS is configured in phpcs.xml (WordPress ruleset, targets classes/) and
PHPMD in phpmd.xml.
Both carry a large backlog inherited from before WPCS 3 — around 1250 PHPCS
findings and 80 PHPMD ones across classes/ — so CI reports them without
failing the build. ./bin/test.sh lint-changed narrows the report to what your
branch touches, which is the useful signal day to day. Roughly 1050 of the PHPCS
findings are auto-fixable with ./bin/test.sh phpcbf.
Source lives in frontend/src/ (TypeScript + SCSS, built with Vite); output goes
to static/. Never edit static/js/main.min.js or static/css/main.min.css
directly.
./bin/test.sh build # compiles frontend/src → static/The build runs the frontend tests first and refuses to write static/ if any
fail. Committing a frontend change means committing the rebuilt static/ output
along with it.
.github/workflows/ci.yml runs on pushes to master
and on every pull request:
| Job | What it does | Gates the build |
|---|---|---|
| Frontend | npm ci, tsc --noEmit, vitest, production build |
yes |
| PHPUnit | Test suite on PHP 8.2 and 8.4 against a MariaDB service | yes |
| Lint | PHPCS + PHPMD, changed files and full report | no — see above |
It runs the same scripts bin/test.sh does, so a green local run means a green
CI run.
A release is a GitHub release whose tag is the bare version number (3.0.25,
not v3.0.25).
-
Bump the version:
./bin/bump-version.sh # patch: 3.0.25 → 3.0.26 ./bin/bump-version.sh --minor # or --major
The script reads the latest published GitHub release and writes the next version to
style.cssand toWP_SOFTCATALA_VERSIONinfunctions.php. The version is also the cache-busting query string on the theme's CSS and JS, so browsers only fetch new assets after a bump. -
Commit the bump and push to
master. It can go in the last change's own commit. -
Publish a release that points at that commit, tagged with the same version:
gh release create 3.0.26 --target master --title "Publicitat per seccions" --notes ""
The title is a few words, in Catalan, naming what the release brings, e.g. "Publicitat per seccions", "Llistes i SEO". A release that only fixes the one before it takes that title plus "(correcció)": "Etiqueta de Clickio (correcció)". The notes stay empty, since the commit log already describes the changes, and releases are never marked as pre-releases.
Publish each release before bumping again. The script works from the latest published release, so running it twice without publishing produces the same number both times.
The theme separates PHP logic from presentation:
- PHP templates (root
*.phpfiles) — WordPress template hierarchy entry points. They set up context and render Twig templates. - Twig templates (
templates/) — presentation layer. All HTML output lives here. - Classes (
classes/) — business logic, custom post types, widgets, services. - REST API (
rest/) — custom WP REST endpoints (downloads updater, projects CSV). - WP-CLI commands (
wp-cli/) — data migration and management scripts, autoloaded whenWP_CLIis defined. - Includes (
inc/) — legacy procedural code: widgets, post type helpers, rewrites, AJAX handlers. - Static assets (
static/) — compiled CSS, JS, fonts, images.
Registered in classes/type-registers/: programa, projecte, esdeveniment, dades-obertes, slider, aparell.
Two autoloading conventions coexist, both registered in functions.php via spl_autoload_register:
SC_prefix — classes likeSC_Foo_Barresolve toclasses/foo-bar.phpSoftcatala\namespace — classes likeSoftcatala\Content\JsonToTableresolve toclasses/content/json-to-table.php(decamelized)
- Downloads updater (
rest/downloads-api.php) — update program download info from external APIs. See REST-API-DOWNLOADS.md for full documentation. - Projects CSV (
rest/projectes-csv-api.php) — projects endpoint.
- Xavi Ivars (@xavivars)
- Pau Iranzo (@paugnu)
- Miquel Piulats (@lequims)
- Jordi Mas (@jordimas)
- Jaume Ortolà (@jaumeortola)