Commit 75386385 authored by Abhishek's avatar Abhishek
Browse files

Merge branch 'new_moodle_file_abhishek' into 'master'

old script file deleted

See merge request !2
parents 121763fc 7723313d
#!/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" <<EOF
-- Enable Web Services globally in Moodle's config
INSERT INTO mdl_config (name, value) VALUES ('enablewebservices', '1') ON DUPLICATE KEY UPDATE value = '1';
INSERT INTO mdl_config (name, value) VALUES ('webserviceprotocols', 'rest') ON DUPLICATE KEY UPDATE value = 'rest';
# --- End of addition --
-- Create our custom service, now with a requiredcapability set for robustness
INSERT IGNORE INTO mdl_external_services (name, shortname, enabled, requiredcapability, restrictedusers, uploadfiles, downloadfiles)
VALUES ('DTA Automation Service', '$CUSTOM_SERVICE_NAME', 1, '', 1, 1, 1); -- <-- The capability fix
-- Get the ID of our custom service
SET @serviceid = (SELECT id FROM mdl_external_services WHERE shortname = '$CUSTOM_SERVICE_NAME');
-- Add the required functions to OUR service
INSERT IGNORE INTO mdl_external_services_functions (externalserviceid, functionname) VALUES
(@serviceid, 'core_course_create_courses'),
(@serviceid, 'enrol_manual_enrol_users'),
(@serviceid, 'core_files_upload');
# -- *** THIS IS THE CRITICAL FIX ***
# -- Authorize the admin user to use this service.
SET @adminuserid = (SELECT id FROM mdl_user WHERE username = '$MOODLE_ADMIN_USER' AND deleted = 0 ORDER BY id ASC LIMIT 1);
INSERT IGNORE INTO mdl_external_services_users (externalserviceid, userid, iprestriction, validuntil, timecreated)
VALUES (@serviceid, @adminuserid, NULL, NULL, UNIX_TIMESTAMP());
EOF
echo "✅ Custom web service created and user authorized."
sleep 5
# --- THIS IS THE CRITICAL FIX ---
echo " -> 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" <<EOF
<?php
define('CLI_SCRIPT', true);
require('/var/www/html/config.php');
require_once(\$CFG->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. 🎉🎉"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment