Technical article · Iforla
The Iforla site: delivery, local stack and enrolment module
This project consisted in rebuilding the website of a language school inside its existing WordPress, then in writing it an enrolment module with quote, emails and PDF, on shared hosting that offers FTP only.
- Role
- Sole author, on behalf of the school
- Period
- From June to September 2026
- Status
- In service. In service
- Technologies
- WordPress, PHP, MariaDB, Docker Compose, GlitchTip
- Repositories
- Two
See the projectVisit the site (opens in a new tab)
Context
The school teaches languages online and in person. The work was done in two stages. A static site of four pages, accompanied by a brand specification, first settled the design; that design was then rebuilt in the school’s WordPress, as a child theme and blocks that its team can edit. An enrolment module was written last, with its price grids, its quote, its emails and its PDF.
The site is in service at iforla.com (opens in a new tab); the school’s data do not appear in this article, which keeps to the way of working.

Problem
The site’s hosting accepts FTP only: no remote access by keys, no staging environment. A delivery there is therefore an upload of files to a site in service, with no safety net. It also had to be possible to reproduce on the workstation the exact state of production, whereas WordPress keeps its addresses in the database in several forms.
Answer
A controlled upload
Each upload follows the same order: download the live version into a dated backup directory, send the files, download them again, then compare their hash with that of the local files. The site carries a single build number, raised at each delivery, displayed in the administration and recorded in a log with the scope of the delivery and its verification. The scripts that delete data do nothing without an explicit argument: run by default, they merely list what they would do.
A local stack realigned with production
The local stack is rebuilt from a backup of production. The script starts by backing up the local database, so that it is possible to go back, then rewrites the addresses in four passes, since the block editor keeps in its contents addresses whose slashes are escaped.
# The block editor stores slash-escaped URLs inside post_content JSON, so the
# plain pass alone leaves half of them behind. WP-CLI's "Replacements" column
# counts ROWS, not occurrences: a low number does not mean a pass was useless.
echo "==> 3/6 rewriting https://$PROD_HOST -> $SITE_URL (4 passes)"
for from in "https://$PROD_HOST" "http://$PROD_HOST"; do
wp search-replace "$from" "$SITE_URL" --all-tables --skip-columns=guid --report-changed-only
done
for from in "https:\\/\\/$PROD_HOST" "http:\\/\\/$PROD_HOST"; do
wp search-replace "$from" "http:\\/\\/localhost:8080" --all-tables --skip-columns=guid --report-changed-only
doneCode of my own where the plugin was not enough
The forms plugin in place did not display multi-step flows in its free version. The enrolment module replaced it: it is driven by a single schema file, has its own table, keeps the price of an enrolment when the grids change, and applies a retention period through a daily task, in addition to the export and the erasure that WordPress provides for personal data. Two gaps in the platform were filled in the same way, namely a hidden language that remained reachable by its address and the redirects from old addresses that WordPress refuses to pages.

