dirroot.'/user/lib.php'); require_once($CFG->libdir.'/accesslib.php'); $systemcontext = context_system::instance(); // 1) Role `wssync` at system context with the exact caps the sync needs. $capabilities = [ 'moodle/user:create', 'moodle/user:viewdetails', // Required so core_user_get_users_by_field returns the username/email // fields it was queried by (user_get_user_details strips them otherwise // and the WS silently returns []). 'moodle/user:viewalldetails', // Required so lookups by field=email get the email field back: // outside a course user_get_user_details() only exposes email through // the site identity fields ($CFG->showuseridentity includes email), // gated by this cap — Task 4 ensureMoodleUser depends on it. 'moodle/site:viewuseridentity', 'moodle/user:update', 'enrol/manual:enrol', 'moodle/course:overridecompletion', 'moodle/course:viewparticipants', 'webservice/rest:use', // login/token.php for a non-mobile service requires createtoken (the // mobile service only needs createmobiletoken, allowed by default). 'moodle/webservice:createtoken', ]; $role = $DB->get_record('role', ['shortname' => 'wssync']); if (!$role) { $roleid = create_role('WS Sync', 'wssync', 'Privileged web-service role for formations-app progression sync', ''); $role = $DB->get_record('role', ['id' => $roleid], '*', MUST_EXIST); echo "role created: {$role->id}\n"; } else { echo "role exists: {$role->id}\n"; } set_role_contextlevels($role->id, [CONTEXT_SYSTEM]); foreach ($capabilities as $cap) { assign_capability($cap, CAP_ALLOW, $role->id, $systemcontext->id, true); echo "cap allowed: {$cap}\n"; } $systemcontext->mark_dirty(); // 2) Service user `wssync` $username = 'wssync'; $password = getenv('WSSYNC_PASSWORD'); if (!$password) { fwrite(STDERR, "WSSYNC_PASSWORD required\n"); exit(1); } $user = core_user::get_user_by_username($username); if (!$user) { $u = new stdClass(); $u->username = $username; $u->password = $password; $u->firstname = 'WS'; $u->lastname = 'Sync'; $u->email = 'wssync@saillant.cc'; $u->confirmed = 1; $u->mnethostid = $CFG->mnet_localhost_id; $uid = user_create_user($u, true, false); $user = core_user::get_user($uid); echo "user created: {$user->id}\n"; } else { echo "user exists: {$user->id}\n"; } // 3) Assign the role at system context (idempotent: role_assign checks dups). role_assign($role->id, $user->id, $systemcontext->id); echo "role assigned at system context\n"; // 4) Dedicated external service. The stock moodle_mobile_app service does // NOT expose core_completion_override_activity_completion_status, so the // sync token must be issued against this service instead // (login/token.php?service=formations_sync). require_once($CFG->dirroot.'/webservice/lib.php'); $wsmanager = new webservice(); $servicefunctions = [ 'core_user_get_users_by_field', 'core_user_create_users', 'core_user_update_users', 'enrol_manual_enrol_users', 'core_course_get_contents', 'core_completion_override_activity_completion_status', ]; $service = $DB->get_record('external_services', ['shortname' => 'formations_sync']); if (!$service) { $serviceid = $wsmanager->add_external_service((object)[ 'name' => 'Formations Sync', 'shortname' => 'formations_sync', 'enabled' => 1, 'restrictedusers' => 1, 'downloadfiles' => 0, 'uploadfiles' => 0, ]); $service = $DB->get_record('external_services', ['id' => $serviceid], '*', MUST_EXIST); echo "service created: {$service->id}\n"; } else { echo "service exists: {$service->id}\n"; } foreach ($servicefunctions as $fn) { if (!$DB->record_exists('external_services_functions', ['externalserviceid' => $service->id, 'functionname' => $fn])) { $wsmanager->add_external_function_to_service($fn, $service->id); echo "service fn added: {$fn}\n"; } else { echo "service fn exists: {$fn}\n"; } } if (!$DB->record_exists('external_services_users', ['externalserviceid' => $service->id, 'userid' => $user->id])) { $wsmanager->add_ws_authorised_user((object)[ 'externalserviceid' => $service->id, 'userid' => $user->id, ]); echo "user authorised on service\n"; } else { echo "user already authorised on service\n"; } echo "OK\n";