This documents how to restore the course from the backups in course_backup/
if the running Xerte instance loses the project or the container is wiped.
| File | What it is |
|---|---|
data.xml |
The published project content — what play.php and the SCORM export read. This is the source of truth for the course. |
preview.xml |
The editor's working copy (kept in sync with data.xml). Restore this too so the editor opens showing all pages. |
Secure_code_development_scorm.zip |
The final SCORM 1.2 export (13 MB, one SCO, 80% pass mark). You can upload this directly to an LMS without restoring the Xerte project at all. |
The course has no uploaded media (the media/ folder only contains the
default readme.txt), so there are no media files to restore.
- Container:
xerte(image tagxerte), port 8081 (was 8080; the host port mapping is set by-p 8081:80), named volumesxerte-data(MariaDB) andxerte-files(/var/www/xerte/USER-FILES). - Project folder on disk:
/var/www/xerte/USER-FILES/1-guest2-Nottingham/data.xml— published content (read byplay.phpand SCORM export)preview.xml— editor working copymedia/— uploaded media (empty here)data.xml.N/preview.xml.N/*.json.N— Xerte's internal numbered backups/snapshots (created automatically on save/publish; you can ignore these when restoring — they're not required).
- Database rows (MariaDB, database
xerte):templatedetails:template_id=1, template_type_id=5, template_name='Secure_code_development', creator_id=1, access_to_whom='Public'(template_type_id=5= the Nottingham template, frameworkxerte.)templaterights:template_id=1, user_id=1, role='creator', folder=1(user_id=1=guest2, the default Guest user.)
-
The
xerteDocker container running on port 8080. If it was removed, start it again (the named volumesxerte-dataandxerte-filespreserve data acrossdocker stop/start; onlydocker volume rmordocker rm -f+ removing volumes wipes them):docker start xerte # if it exists but is stopped # OR, if the container was removed but volumes kept: docker run -d --name xerte -p 8081:80 \ -v xerte-data:/var/lib/mysql \ -v xerte-files:/var/www/xerte/USER-FILES \ -e XERTE_FRESHCLAM_ON_START=false \ xerte # Wait ~25s for first-run provisioning, then check: curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8081/
-
dockerandcurlon the host. The backups are in/Users/robosan/gitclones/xerteonlinetoolkits/course_backup/(adjust the path if you copied them elsewhere).
Use this if the container is running and the project folder still exists but the content is missing/corrupted and you want to put the backed-up content back.
-
Copy the backed-up XML into the container, removing any lockfile:
docker cp course_backup/data.xml xerte:/var/www/xerte/USER-FILES/1-guest2-Nottingham/data.xml docker cp course_backup/preview.xml xerte:/var/www/xerte/USER-FILES/1-guest2-Nottingham/preview.xml docker exec xerte sh -c 'rm -f /var/www/xerte/USER-FILES/1-guest2-Nottingham/lockfile.txt'
-
Fix ownership/permissions so Apache (www-data) can read/write:
docker exec xerte sh -c 'chown www-data:www-data /var/www/xerte/USER-FILES/1-guest2-Nottingham/data.xml /var/www/xerte/USER-FILES/1-guest2-Nottingham/preview.xml; chmod 664 /var/www/xerte/USER-FILES/1-guest2-Nottingham/data.xml /var/www/xerte/USER-FILES/1-guest2-Nottingham/preview.xml'
-
Verify it plays and exports:
curl -s -o /dev/null -w "play=%{http_code}\n" "http://localhost:8081/play.php?template_id=1" # expect play=200
Open
http://localhost:8081/play.php?template_id=1in a browser to confirm all 48 pages and the quizzes render.
The editor will load the restored preview.xml (clear the lockfile warning if
prompted): http://localhost:8081/edithtml.php?template_id=1.
Use this if the 1-guest2-Nottingham folder no longer exists (e.g. the project
was deleted in Xerte, or docker volume rm xerte-files was run). You recreate
the empty project, then restore the content into it.
If the DB was wiped and reprovisioned, Xerte re-creates the built-in templates on startup, so the Nottingham template (template_type_id=5) should already exist. Confirm:
docker exec xerte sh -c 'mariadb --socket=/run/mysqld/mysqld.sock -uroot -proot xerte -e "SELECT template_type_id, template_framework, template_name FROM originaltemplatesdetails WHERE template_name=\"Nottingham\";"'
# expect: 5 xerte NottinghamIf the row is missing, the container did not provision correctly — re-run the
container and wait for "Provisioning complete" in docker logs xerte.
Guest auto-login means no credentials are needed. Create the project with a single POST:
COOKIE=/tmp/x.cookies
curl -s -c "$COOKIE" http://localhost:8081/index.php -o /dev/null
RESP=$(curl -s -b "$COOKIE" -c "$COOKIE" -X POST \
-d "templatename=Nottingham&tutorialname=Secure code development" \
http://localhost:8081/website_code/php/templates/new_template.php)
echo "Create response: $RESP" # e.g. "1,1280, 768" — the first number is the template_id
NEW_ID=$(echo "$RESP" | cut -d, -f1)
echo "New template_id: $NEW_ID"This creates a folder /var/www/xerte/USER-FILES/<NEW_ID>-guest2-Nottingham/
with a fresh empty data.xml and preview.xml. Note the <NEW_ID>.
The folder name is <NEW_ID>-guest2-Nottingham (e.g. 2-guest2-Nottingham).
Copy the backed-up XML into it:
NEW_ID=2 # replace with the id from B2
FOLDER="/var/www/xerte/USER-FILES/${NEW_ID}-guest2-Nottingham"
docker cp course_backup/data.xml "xerte:${FOLDER}/data.xml"
docker cp course_backup/preview.xml "xerte:${FOLDER}/preview.xml"
docker exec xerte sh -c "chown www-data:www-data '${FOLDER}/data.xml' '${FOLDER}/preview.xml'; chmod 664 '${FOLDER}/data.xml' '${FOLDER}/preview.xml'; rm -f '${FOLDER}/lockfile.txt'"Newly created projects default to Private. Set it Public so play.php works
without a login (matching the backed-up state):
docker exec xerte sh -c "mariadb --socket=/run/mysqld/mysqld.sock -uroot -proot xerte -e \"UPDATE templatedetails SET access_to_whom='Public' WHERE template_id=${NEW_ID};\""
curl -s -o /dev/null -w "play=%{http_code}\n" "http://localhost:8081/play.php?template_id=${NEW_ID}"
# expect play=200Open http://localhost:8081/edithtml.php?template_id=${NEW_ID} in a browser
(click "Delete lockfile and continue" if prompted). Confirm the page tree shows
all 48 pages. Click Publish once so Xerte stamps its internal state from the
restored preview.xml into data.xml and regenerates its numbered backups.
If you just want to deliver the course to an LMS, you do not need to restore the Xerte project at all. Upload the backed-up SCORM package directly to the LMS:
- File:
course_backup/Secure_code_development_scorm.zip - Format: SCORM 1.2, one SCO (
scormRLO.htm), 80% pass mark, LMS score reporting viaapiwrapper_1.2.js+xttracking_scorm1.2.js.
The LMS will import it as a single SCORM 1.2 activity.
Once the project is restored and play.php?template_id=<ID> returns 200, you
can re-export a fresh SCORM zip (a guest session cookie is required):
COOKIE=/tmp/x.cookies
curl -s -c "$COOKIE" http://localhost:8081/index.php -o /dev/null
curl -sL -b "$COOKIE" -o Secure_code_development_scorm.zip \
-w "export HTTP=%{http_code}, size=%{size_download} bytes\n" \
"http://localhost:8081/website_code/php/scorm/export.php?scorm=true&template_id=<ID>"
# replace <ID> with 1 (Scenario A) or the NEW_ID from B2
unzip -l Secure_code_development_scorm.zip | head # expect imsmanifest.xml, scormRLO.htm, ...Quick checks that the content is intact:
# extract the project data from the zip (Nottingham stores it as template.xml)
unzip -p Secure_code_development_scorm.zip template.xml > /tmp/check.xml
grep -c "Trial MCQ" /tmp/check.xml # expect 0 (trial removed)
grep -oE "<question" /tmp/check.xml | wc -l # expect 41 (23 theme + 18 final)
grep -oE "trackingPassed=\"[^\"]*\"" /tmp/check.xml # expect trackingPassed="80%"
grep -c "Final comprehensive quiz" /tmp/check.xml # expect 1
# all 20 OWASP items present:
for i in "Broken Access Control" "Injection" "Cryptographic Failures" "Security Misconfiguration" \
"Insecure Design" "Authentication Failures" "Software Supply Chain Failures" \
"Software or Data Integrity Failures" "Security Logging and Alerting Failures" \
"Mishandling of Exceptional Conditions" "Agent Goal Hijack" "Tool Misuse" \
"Identity and Privilege Abuse" "Agentic Supply Chain" "Unexpected Code Execution" \
"Memory" "Insecure Inter-Agent Communication" "Cascading Failures" \
"Human-Agent Trust" "Rogue Agents"; do
grep -qi "$i" /tmp/check.xml && echo "OK $i" || echo "MISSING $i"
done- Folder naming: the on-disk folder is
<template_id>-guest2-Nottingham. If you recreate the project (Scenario B) the newtemplate_idwill likely differ from 1 — use the new id everywhere (play.php,edithtml.php, the export URL, the DB update). data.xmlvspreview.xml:play.phpand SCORM export readdata.xml. The editor reads/writespreview.xmland copies it todata.xmlon Publish. Restoring both keeps them in sync; if you only restoredata.xml, open the editor and Publish once to regeneratepreview.xmlfrom it.- Lockfile: if you see "This project is currently locked" when opening the
editor, click "Delete lockfile and continue to editor", or
docker exec xerte rm -f /var/www/xerte/USER-FILES/<ID>-guest2-Nottingham/lockfile.txt. - Guest user: the project is owned by
guest2(user_id=1). Guest auto-login is on by default, so no login is needed to view/edit/export. - Numbered
.Nfiles (data.xml.1,preview.json.N, etc.) are Xerte's own rolling snapshots — you do not need to back up or restore them; they are regenerated as you edit/publish. - Volumes:
docker stop xerte/docker start xertekeeps everything.docker rm -f xertekeeps the volumes (so the project survives) unless you also rundocker volume rm xerte-data xerte-files.