diff --git a/test_moodle.sh b/test_moodle.sh deleted file mode 100644 index 0bd6e19be027c6c3ff1e3b09083932f05414aa3f..0000000000000000000000000000000000000000 --- a/test_moodle.sh +++ /dev/null @@ -1,289 +0,0 @@ -#!/bin/bash -set -e - -# ============================================================================== -# DEFINITIVE Moodle CI/CD AUTOMATION SCRIPT V10 (ROBUST ERROR CHECKING) -# ============================================================================== - -# --- Configuration (UPDATE THESE!) --- -MOODLE_URL="http://localhost:8080" -MOODLE_ADMIN_USER="user" # <-- IMPORTANT: CHANGE THIS -MOODLE_ADMIN_PASS="QAYwsx@12345" # <-- IMPORTANT: CHANGE THIS -DTA_CONFIG_FILE="teacher-dta-dir-test-runner-jdk21.txt" - -# --- Static Configuration --- -MOODLE_CONTAINER="moodle-web-official" -MOODLE_SYSTEM_USER="www-data" -MOODLE_ROOT_IN_CONTAINER="/var/www/html" -PLUGIN_ZIP_NAME="dta.zip" -CUSTOM_SERVICE_NAME="dta_automation_service" -DB_CONTAINER_NAME="moodle-db-official" -DB_ROOT_PASSWORD="verysecretpassword" -DB_NAME="moodle" - -# ============================================================================== -# SCRIPT EXECUTION -# ============================================================================== - -echo "🚀 Starting Definitive Moodle Automation Script..." - -# --- Pre-flight Checks & Plugin Install (Step 1) --- -if ! docker ps --format '{{.Names}}' | grep -qw "$MOODLE_CONTAINER"; then echo "❌ Moodle container not running." && exit 1; fi -if [ ! -f "$PLUGIN_ZIP_NAME" ]; then echo "❌ Plugin ZIP file not found." && exit 1; fi -if [ ! -f "$DTA_CONFIG_FILE" ]; then echo "❌ DTA config file not found." && exit 1; fi -echo "✅ Pre-flight checks passed." - -echo "📦 Step 1: Installing custom plugin..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" bash -c "set -e; mkdir -p '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; unzip -o '/tmp/$PLUGIN_ZIP_NAME' -d '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/upgrade.php' --non-interactive; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php'" -echo "✅ Plugin installed." - -# --- Step 2: Create and Configure a Custom Web Service --- -echo " -> Clearing cURL security settings..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityblockedhosts --set="" -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityallowedport --set="" - -echo " -> Enabling Web Service File Uploads (System-wide)..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=webserviceuploaddisabled --set=0 -# --- THIS IS THE FINAL FIX --- -# Enable username/password authentication for web service token generation -echo " -> Enabling password authentication for web services..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=enablewsauthpassword --set=1 - - -echo "⚙️ Step 2: Creating and configuring a dedicated web service..." -docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" < Purging Moodle caches to apply service changes..." -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" -echo "✅ Caches purged." -sleep 5 - -# --- Step 3: Get API Token (with Robust Checking) --- -echo "🔐 Step 3: Obtaining API Token...$MOODLE_ADMIN_USER..$MOODLE_ADMIN_PASS" -TOKEN_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/login/token.php" \ - --data-urlencode "username=$MOODLE_ADMIN_USER" \ - --data-urlencode "password=$MOODLE_ADMIN_PASS" \ - --data-urlencode "service=$CUSTOM_SERVICE_NAME") - -# Check for a Moodle error in the response FIRST -if echo "$TOKEN_RESPONSE" | jq -e 'if type=="object" and .error then true else false end' > /dev/null; then - echo "❌ Moodle API returned an error when requesting a token:" - echo "$TOKEN_RESPONSE" | jq . - exit 1 -fi - -TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token') -echo "✅ Successfully obtained API token. $TOKEN" - -# --- Step 4: Create a Course --- -echo "📚 Step 4: Creating a new course..." -COURSE_SHORTNAME="DTA-Course-$(date +%s)" -COURSE_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" -d "wsfunction=core_course_create_courses" -d "moodlewsrestformat=json" \ - -d "courses[0][fullname]=DTA Test Course" -d "courses[0][shortname]=$COURSE_SHORTNAME" -d "courses[0][categoryid]=1") - -# --- THIS IS THE ROBUST ERROR CHECK --- -# First, check if the response is a Moodle exception object. -# The `jq -e` command sets an exit code, which works perfectly with `if`. -if echo "$COURSE_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then - echo "❌ Moodle API returned an exception during course creation:" - echo "$COURSE_RESPONSE" | jq . # Pretty-print the JSON error - exit 1 -fi - -# If not an exception, try to parse the ID. This handles both single-object and array responses. -NEW_COURSE_ID=$(echo "$COURSE_RESPONSE" | jq -r 'if type=="array" then .[0].id else .id end') - -if [ -z "$NEW_COURSE_ID" ] || [ "$NEW_COURSE_ID" == "null" ]; then - echo "❌ Failed to parse a valid Course ID from the API response." - echo "Full API Response:" - echo "$COURSE_RESPONSE" | jq . - exit 1 -fi -echo "✅ Successfully created course '$COURSE_SHORTNAME' with ID: $NEW_COURSE_ID" -# --- END OF ROBUST ERROR CHECK --- - -# --- Step 5: Enroll Admin User into the New Course --- -echo "🧑‍🏫 Step 5: Enrolling you as a teacher in the new course..." -# First, get the User ID for your admin user -ADMIN_USER_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_user WHERE username='$MOODLE_ADMIN_USER';") -# The roleid for a Teacher is typically 3 (editingteacher) or 4 (teacher). We'll use 3. -TEACHER_ROLE_ID=3 - -# Add the enrol_manual_enrol_users function to our service -docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -e "INSERT IGNORE INTO mdl_external_services_functions (externalserviceid, functionname) VALUES ((SELECT id FROM mdl_external_services WHERE shortname = '$CUSTOM_SERVICE_NAME'), 'enrol_manual_enrol_users');" -# Purge cache to recognize the new function -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" > /dev/null - -ENROL_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \ - -d "wstoken=$TOKEN" -d "wsfunction=enrol_manual_enrol_users" -d "moodlewsrestformat=json" \ - -d "enrolments[0][roleid]=$TEACHER_ROLE_ID" \ - -d "enrolments[0][userid]=$ADMIN_USER_ID" \ - -d "enrolments[0][courseid]=$NEW_COURSE_ID") -# A successful response is null, so we check for an exception instead. -if echo "$ENROL_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then - echo "❌ Moodle API returned an exception during user enrollment:" - echo "$ENROL_RESPONSE" | jq . - exit 1 -fi -echo "✅ Successfully enrolled you in the course." - -# --- Step 5: Upload Configuration File for DTA Plugin --- -echo "📤 Step 5: Uploading config file for DTA plugin..." -FILE_UPLOAD_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/upload.php" \ - -F "token=$TOKEN" -F "filearea=draft" -F "itemid=0" -F "file=@$DTA_CONFIG_FILE") -DTA_FILE_ITEMID=$(echo "$FILE_UPLOAD_RESPONSE" | jq -r 'if type=="array" then .[0].itemid else .itemid end') - -if [ -z "$DTA_FILE_ITEMID" ] || [ "$DTA_FILE_ITEMID" == "null" ]; then - echo "❌ Failed to upload DTA config file. Response:" - echo "$FILE_UPLOAD_RESPONSE" | jq . - exit 1 -fi -echo "✅ DTA config file uploaded. Received Item ID: $DTA_FILE_ITEMID" - -# --- Step 6: Create Assignment via Custom PHP Script --- -echo "📝 Step 6: Creating assignment with detailed error logging..." -PHP_SCRIPT_PATH="/tmp/create_assignment_debug.php" -LOCAL_PHP_SCRIPT_PATH="/tmp/local_create_assignment_debug.php" - -# --- This PHP script now has a try/catch block for detailed errors --- -cat > "$LOCAL_PHP_SCRIPT_PATH" <dirroot.'/course/modlib.php'); -global \$DB, \$USER; - -// programmatically "log in" as the admin user -// This gives file_save_draft_area_files the user context it needs. -\$adminusername = '$MOODLE_ADMIN_USER'; -\$adminuser = \$DB->get_record('user', ['username' => \$adminusername, 'deleted' => 0], '*', MUST_EXIST); -\core\session\manager::set_user(\$adminuser); -// The global $USER object is now populated. - -if (count(\$argv) < 3) { exit("Error: Course ID and DTA Item ID are required.\n"); } -\$courseid = (int)\$argv[1]; -\$dta_itemid = (int)\$argv[2]; - -\$course = \$DB->get_record('course', ['id' => \$courseid], '*', MUST_EXIST); - -\$data = new stdClass(); - -// --- Core Module Details --- -\$data->course = \$courseid; -\$data->modulename = 'assign'; -\$data->name = 'Fully Automated DTA Assignment'; -\$data->intro = 'This assignment was created by the final automation script.'; -\$data->introformat = FORMAT_HTML; -\$data->section = 1; -\$data->visible = 1; - -// --- Submission Settings --- -\$data->assignsubmission_dta_enabled = 1; -\$data->assignsubmission_file_enabled = 1; - -// --- THIS IS FIX #1: Correct property name for the DTA file manager --- -// This assumes the form element for your file manager is named 'package_filemanager' -// The final property name becomes assignsubmission_dta_package_filemanager -\$data->dta = 1; // Explicitly enable the dta group of settings -\$data->tests_draft_dta = \$dta_itemid; - -\$data->assignsubmission_file_maxfiles = 20; - -\$data->assignsubmission_onlinetext_enabled = 0; -\$data->submissiondrafts = 0; // Final submissions cannot be edited -\$data->requiresubmissionstatement = 0; // The direct fix for the error -\$data->requireallteammemberssubmit = 0; -\$data->teamsubmission = 0; - -// --- Notification Settings --- -\$data->sendnotifications = 1; -\$data->sendlatenotifications = 0; -\$data->sendstudentnotifications = 1; - -// --- Grading Settings --- -\$data->grade = 100; // Max grade -\$data->blindmarking = 0; -\$data->markingworkflow = 0; -\$data->markingallocation = 0; -\$data->attemptreopenmethod = 'none'; - -// --- Date Settings --- -\$data->allowsubmissionsfromdate = time(); -\$data->duedate = time() + (7 * 24 * 60 * 60); // Due in 7 days -\$data->cutoffdate = 0; // No hard cutoff -\$data->gradingduedate = 0; - -// --- Moodle Magic - these fields are necessary but we don't need to change them --- -\$data->module = \$DB->get_field('modules', 'id', ['name' => 'assign'], MUST_EXIST); -\$data->timemodified = time(); -\$data->alwaysshowdescription = 0; -\$data->completionsubmit = 0; - -// --- DEBUG: Print the exact data object we are sending --- -echo "DEBUG: Data object being sent to add_moduleinfo():\n"; -print_r(\$data); -echo "\n"; - -try { - // Attempt to create the assignment - \$cm = add_moduleinfo(\$data, \$course); - - if (!\$cm || !is_object(\$cm) || !\$cm->id) { - exit("Error: add_moduleinfo() returned a non-valid object but did not throw an exception.\n"); - } - - rebuild_course_cache(\$course->id, true); - echo "Successfully created assignment with Course Module ID: \$cm->id\n"; - -} catch (Exception \$e) { - // If anything goes wrong, catch the exception and print all details - echo "\n!!!!!! SCRIPT FAILED: An Exception was caught! !!!!!!\n\n"; - echo "Error Type: " . get_class(\$e) . "\n"; - echo "Error Message: " . \$e->getMessage() . "\n\n"; - - // Moodle's database exceptions often have extra useful debug info - if (!empty(\$e->debuginfo)) { - echo "Moodle Debug Info:\n" . \$e->debuginfo . "\n\n"; - } - - echo "Stack Trace:\n" . \$e->getTraceAsString() . "\n"; - exit(1); // Exit with an error code -} -EOF - -docker cp "$LOCAL_PHP_SCRIPT_PATH" "$MOODLE_CONTAINER:$PHP_SCRIPT_PATH" -docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$PHP_SCRIPT_PATH" "$NEW_COURSE_ID" "$DTA_FILE_ITEMID" -docker exec "$MOODLE_CONTAINER" rm "$PHP_SCRIPT_PATH" -rm "$LOCAL_PHP_SCRIPT_PATH" -echo "✅ Assignment created successfully using the custom PHP script." - -echo "🎉🎉 Moodle automation complete. The environment is fully configured. 🎉🎉"