username = 'testusername' . $n; $user->password = 'testpassword' . $n; $user->firstname = 'testfirstname' . $n; $user->lastname = 'testlastname' . $n; $user->email = 'testemail' . $n . '@moodle.com'; $user->auth = 'manual'; $user->idnumber = 'testidnumber' . $n; $user->lang = 'en'; $user->theme = 'standard'; $user->timezone = '0'; $user->mailformat = 0; $user->description = 'Hello World!'; $user->city = 'testcity' . $n; $user->country = 'uk'; return $user; } /* Returns a structure defining a test course whose name etc. end in $n. I have set the category ID to 1. This works, but is almost certainly wrong. I need to find out what it should be. */ function make_test_course( $n ) { $course = new stdClass(); $course->fullname = 'testcourse' . $n; $course->shortname = 'testcourse' . $n; $course->categoryid = 1; return $course; } /* Creates a user from a structure defining a user. If the creation succeeds, returns the ID for this user. If not, reports an error. */ function create_user( $user, $token ) { $users = array( $user ); $params = array( 'users' => $users ); $response = call_moodle( 'core_user_create_users', $params, $token ); if ( xmlresponse_is_exception( $response ) ) { echo "\nFunction returned an error.\n"; } else { $user_id = xmlresponse_to_id( $response ); return $user_id; } } /* Returns a user data structure containing Moodle's data for $user_id. It generates this by parsing the XML that Moodle returns. If Moodle thinks there is no such user, returns NULL. */ function get_user( $user_id, $token ) { $userids = array( $user_id ); $params = array( 'userids' => $userids ); $response = call_moodle( 'core_user_get_users_by_id', $params, $token ); $user = xmlresponse_to_user( $response ); if ( array_key_exists( 'id', $user ) ) return $user; else return NULL; // If there is no user with this ID, Moodle // returns the same enclosing XML as if there were, but // with no values for ID and the other fields. My // XML-parsing code therefore creates an object // with no fields, which the conditional above // detects. } /* Deletes the user with ID $user_id. */ function delete_user( $user_id, $token ) { $userids = array( $user_id ); $params = array( 'userids' => $userids ); $response = call_moodle( 'core_user_delete_users', $params, $token ); } /* Assigns the role with $role_id to the user with $user_id in the specified context. */ function assign_role( $user_id, $role_id, $context_id, $token ) { $assignment = array( 'roleid' => $role_id, 'userid' => $user_id, 'contextid' => $context_id ); $assignments = array( $assignment ); $params = array( 'assignments' => $assignments ); $response = call_moodle( 'core_role_assign_roles', $params, $token ); } /* Creates a course from a structure defining a course. If the creation succeeds, returns the ID for this course. If not, reports an error. */ function create_course( $course, $token ) { $courses = array( $course ); $params = array( 'courses' => $courses ); $response = call_moodle( 'core_course_create_courses', $params, $token ); if ( xmlresponse_is_exception( $response ) ) { echo "\nFunction returned an error.\n"; } else { $course_id = xmlresponse_to_id( $response ); return $course_id; } } /* Enrols the user into the course with the specified role. */ function enrol( $user_id, $course_id, $role_id, $token ) { $enrolment = array( 'roleid' => $role_id, 'userid' => $user_id, 'courseid' => $course_id ); $enrolments = array( $enrolment ); $params = array( 'enrolments' => $enrolments ); $response = call_moodle( 'enrol_manual_enrol_users', $params, $token ); } /* Returns data about users enrolled in the specified course. Only works if there is only one user. */ function get_enrolled_users( $course_id, $token ) { $params = array( 'courseid' => $course_id ); $response = call_moodle( 'core_enrol_get_enrolled_users', $params, $token ); $user = xmlresponse_to_user( $response ); return $user; } /* Calls the Moodle at $domain, invoking the specified function on $params. Also takes a token. Returns Moodle's response as a string containing XML. */ function call_moodle( $function_name, $params, $token ) { global $domain; $serverurl = $domain . '/webservice/rest/server.php'. '?wstoken=' . $token . '&wsfunction='.$function_name; require_once( './curl.php' ); $curl = new curl; $response = $curl->post( $serverurl . $restformat, $params ); if ( TRACING ) echo "\nResponse from $function_name: \n", $response, ".\n"; return $response; } /* Given a string containing XML returned by a successful user creation or course creation, parses it and returns the user or course ID as an integer. Undefined if the XML does not contain such an ID, for example if it's an error response. */ function xmlresponse_to_id( $xml_string ) { $xml_tree = new SimpleXMLElement( $xml_string ); $value = $xml_tree->MULTIPLE->SINGLE->KEY->VALUE; $id = intval( sprintf( "%s", $value ) ); // See discussion on http://php.net/manual/es/book.simplexml.php , // especially the posting for "info at kevinbelanger dot com 20-Jan-2011 05:07". // There is a bug in the XML parser whereby it doesn't return the // text associated with property [0] of a node. The above // posting uses sprintf to force a conversion to string. return $id; } /* Given a string containing XML returned by a successful call to core_user_get_users_by_id, parses it and returns the data as a user data structure. Undefined if the XML does not contain such an ID, for example if it's an error response. Does not handle fields with multiple values. I think these are customfields, preferences, and enrolledcourses. */ function xmlresponse_to_user( $xml_string ) { return xmlresponse_parse_names_and_values( $xml_string ); } /* This parses a string containing the XML returned by functions such as core_course_get_courses, core_user_get_users_by_id, or core_enrol_get_enrolled_users. These strings contain name-value pairs encoded thus: 169 testusername32 The function returns an object with the corresponding keys and values. Does not convert strings to integers where they ought to be converted. */ function xmlresponse_parse_names_and_values( $xml_string ) { $xml_tree = new SimpleXMLElement( $xml_string ); $struct = new StdClass(); foreach ( $xml_tree->MULTIPLE->SINGLE->KEY as $key ) { $name = $key['name']; $value = (string)$key->VALUE; $struct->$name = $value; } return $struct; } /* True if $xml_string's top-level is . I use this to check for error responses from Moodle. */ function xmlresponse_is_exception( $xml_string ) { $xml_tree = new SimpleXMLElement( $xml_string ); $is_exception = $xml_tree->getName() == 'EXCEPTION'; return $is_exception; } /* These are the manager and student roles from my Moodle, obtained by querying the database with the command select * from mdl_role; */ define( "MANAGER_ROLE_ID", 1 ); define( "STUDENT_ROLE_ID", 5 ); /* This is the context ID from my Moodle, obtained by querying the database with the command select * from mdl_context. */ define( "SYSTEM_CONTEXT_ID", 1 ); /* Demonstrate the bug. */ function demo() { global $token, $user_suffix, $course_suffix; try { echo "Demo of Moodle bug\n"; echo "==================\n"; echo "\nUses this token which I created manually: " . $token . ".\n"; echo "\nWill now create a user from the following data:\n"; $user_data = make_test_user( $user_suffix ); print_r( $user_data ); print("."); echo "\n"; $user_id = create_user( $user_data, $token ); echo "\nUser's ID = " . $user_id . ".\n"; echo "\nWill now assign a role to make user a manager.\n"; assign_role( $user_id, MANAGER_ROLE_ID, SYSTEM_CONTEXT_ID, $token ); echo "\nInspecting my Moodle's 'Assign system roles' showed that although 'enrol' gave an error, the user did receive this role.\n"; echo "\nWill now create a course from the following data:\n"; $course_data = make_test_course( $course_suffix ); print_r( $course_data ); print(".\n"); $course_id = create_course( $course_data, $token ); echo "\nCourse ID = " . $course_id . ".\n"; echo "\nWill now enrol user.\n"; $role_id = STUDENT_ROLE_ID; enrol( $user_id, $course_id, $role_id, $token ); echo "\nWill now get the ID of who Moodle thinks is enrolled.\n"; $user_in_course = get_enrolled_users( $course_id, $token ); echo "\nUser details = \n"; print_r( $user_in_course ); print(".\n"); echo "\nThis shows that on my Moodle, although 'enrol' gave an error, the user did get enrolled.\n"; echo "\nWill now try deleting the user.\n"; delete_user( $user_id, $token ); echo "\nWill now get the user details using user's ID.\n"; $user_data_from_moodle = get_user( $user_id, $token ); echo "\nUser details = \n"; print_r( $user_data_from_moodle ); print(".\n"); echo "\nThis shows that on my Moodle, although 'delete' gave an error, the user no longer exists.\n"; } catch ( Exception $e ) { echo "\nCaught exception:\n" . $e->getMessage() . "\n"; } } demo();