User synchronization¶
Profile fields created automatically¶
On install/upgrade, auth_suap_bulk_user_custom_field() (db/migrate.php, called by
db/install.php and db/upgrade.php) creates the profile field categories SUAP,
Dados pessoais (Personal data), Dados de contato (Contact data), Matrícula
(Enrollment), Polo (Hub/Pole), Campus, Curso (Course) and Turma (Class), and
registers the fields:
tipo_usuario,eh_servidor,eh_aluno,eh_prestador,eh_usuarioexterno,eh_docente,eh_tecnico_administrativo,last_loginnome_apresentacao,nome_completo,nome_social,data_de_nascimento,sexo,cpf,rg,passaporte,naturalidade,filiacao_mae,filiacao_pai,id_doc_certificado,tipo_doc_certificado,eh_estrangeiroemail_google_classroom,email_academico,email_secundarioprograma_nome,ingresso_periodo,outras_matriculas,situacao_vinculo,matricula_regular,vinculo_ativo,vinculo_cargo,vinculo_categoria,ira,matriz_curricularpolo_id,polo_nome,polo_siglacampus_id,campus_descricao,campus_siglacurso_id,curso_codigo,curso_descricao,curso_modalidade_id,curso_modalidade_descricao,curso_modalidade,curso_nivel_ensino_id,curso_nivel_ensino_descricao,curso_nivel_ensinoturma_id,turma_codigo
All fields are created with locked = 1 (locked for editing by the user) and
visible = 1, except last_login, which is hidden (visible = 0). At the end, the
lock is replicated (field_lock_profile_field_<shortname> = locked) to all installed
authentication plugins, not just auth_suap.
Fields changed on first login vs. subsequent logins¶
Table based on the creation/update flow in auth.php::create_or_update_user().
Field |
1st login |
Subsequent |
Source / notes |
|---|---|---|---|
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
Random local password; ignored (authentication is always via |
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
|
|
Yes (creation) |
No |
Current timestamp. |
|
Yes (creation) |
No |
Current timestamp. |
|
Yes (creation) |
No |
Remote IP ( |
|
Yes (creation) |
No |
|
|
Yes |
Yes |
Full name chosen among |
|
Yes |
Yes |
Same as above. |
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes (if a photo is available) |
Yes (if a photo is available) |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
Full JSON of the combined SUAP payload; used for support. |
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
Digits only, zero-padded on the left up to 11 digits. Deprecated, but may still be received from SUAP. |
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes |
Yes |
|
|
Yes (if CPF/passport) |
Yes (if CPF/passport) |
Masked CPF ( |
|
Yes (if CPF/passport) |
Yes (if CPF/passport) |
|
|
Yes |
Yes |
From the relationship whose identifier matches the |
|
Yes |
Yes |
|
Updating the user’s photo¶
The photo (user.picture) is handled separately from the other fields, since it requires
downloading a binary file and processing it — potentially slow enough to give the impression
that Moodle itself is hanging. So, unlike the other fields (updated synchronously, in the same
request), the photo download/processing runs in the background, via an ad hoc task, both
on login and in the administrative bulk action described further below.
Queueing (non-blocking)¶
auth_plugin_suap::queue_update_picture_task() (auth.php) is called at the end of
create_or_update_user(), after update_user_record() has already persisted
profile_field_last_login — that is, the SUAP payload is already saved by the time the
task actually runs. The method:
Calls
get_last_login_photo_sources(), which builds the ordered list of candidate URLs from the saved payload, in the priority orderurl_foto_150x200→url_foto_75x100→foto. If no URL is present, it returnsfalsewithout queueing anything.Otherwise, it creates an ad hoc task
auth_suap\task\update_user_picture_adhocwithuseridas custom data and queues it via\core\task\manager::queue_adhoc_task($task, true)— the second argument (checkforexisting) prevents duplicating the task if the same user already has one pending.
The login (and the bulk action) return immediately after queueing; the actual download only happens when cron processes the ad hoc task queue (typically within 1 minute, depending on the site’s cron frequency).
Download and processing (in the background)¶
When cron runs the task, update_user_picture_adhoc::execute()
(classes/task/update_user_picture_adhoc.php) reloads the user by the saved userid,
fetches the candidate URLs with get_last_login_photo_sources() and calls
auth_plugin_suap::update_picture(), which:
Download: goes through the list of candidate URLs and tries to download each one via
auth_suap_curl_get()(5-second timeout), stopping at the first one that returns non-empty content. Download failures (cURL error, HTTP ≥ 400, timeout) on one URL do not prevent trying the next one in the list.Processing: the downloaded content is written to a temporary file (
$CFG->tempdir/suapfoto<user id>) and processed byprocess_new_icon()(Moodle core API), which generates the user icon and returns the image file identifier.Persistence: if
process_new_icon()returns a valid identifier, theuser.picturefield is updated directly via$DB->set_field().
update_picture() returns a bool — true only if user.picture was actually
updated, false at any point of failure. The ad hoc task uses this return value to write
the correct mtrace() message (“completed” or “failed”) to the cron/task execution log —
see the note below on why this matters.
Failure handling¶
No failure in this flow interrupts the login (nor the bulk action) — at worst, the user
simply ends up without an updated photo. Since update_picture() is only called from
background tasks (ad hoc or scheduled — see notes below), no exception bubbles up to cron:
the task execution always shows as completed successfully, even when the photo of one or more
users could not be updated. There are two distinct logging channels to find these failures
despite that:
mtrace()(prefix[AUTH SUAP]used throughout the plugin), for every point of failure, including the technical detail per attempt — error downloading a specific URL (exception fromauth_suap_curl_get(), caught via\Throwable), no URL resulting in valid content, orprocess_new_icon()failing. Shown as Task output of the task execution, always (does not depend on$CFG->debug).the
auth_suap\event\picture_update_failedevent (classes/event/picture_update_failed.php), fired byupdate_picture()only in the two outcomes that actually matter to an administrator — no URL resulted in valid content or ``process_new_icon()`` failed — associated with the context and therelateduseridof the affected user. Recorded by Moodle’s standard log store, filterable by user/event without needing to open a specific task execution.
Note
Failures downloading an individual URL (when there is still another candidate URL in the
list to try) only go through mtrace() — the event is only fired when all URLs are
exhausted without success, so as not to generate a log entry per attempt.
How to find a failure¶
Task logs — Site administration → Server → Tasks → Task logs (
/admin/tasklogs.php). Open the execution ofauth_suap\task\update_user_picture_adhoc(ad hoc, per user) orauth_suap\task\backfill_user_pictures(scheduled, in bulk) and search, in the Task output, for[AUTH SUAP] Falha ao atualizar a foto(Failed to update photo) — the same wording in both tasks, for a reliable search regardless of which one ran. Each occurrence includes the affected user’susername/id. Since the list of executions does not distinguish “success” from “success with internal failures”, you need to open and read the text — you cannot filter only for problematic executions.Logs report — Site administration → Reports → Logs (
/report/log/index.php). Filter by Event name = “Picture update failed (SUAP)” — listed under the “SUAP OAuth2 Authentication” component — or select the specific user in the All participants filter: the Affected user column points to who had a failed photo, and the Description shows the reason. Unlike the Task output, this channel is already filterable by user/event directly in the interface.Live logs — Site administration → Reports → Live logs (
/report/loglive/index.php) shows the same source as item 2 nearly in real time; useful for following an attempt as it happens — for example, right after confirming the bulk action or clicking “Run now” on one of the tasks.
Tip
The first two channels complement each other: the Logs report (item 2) is the fastest way to discover which users had a failure and why; the Task output (item 1) is useful when you already know which execution to investigate and want the full technical detail, including the individual per-URL attempts that don’t generate an event.
Note
auth_plugin_suap::update_picture() remains synchronous by itself — whoever calls it
directly (like the two tasks described on this page, which already run in the background
via cron) still blocks until the download finishes. It is queue_update_picture_task()
that decouples this from the login/bulk-action HTTP request.
Note
update_picture_from_last_login($usuario) is a convenience method that combines
get_last_login_photo_sources() + update_picture() and returns true only on
actual success (false for both “no data” and “tried and failed”). That’s why the two
tasks (backfill_user_pictures and update_user_picture_adhoc) call
get_last_login_photo_sources() and update_picture() separately instead of using
this shortcut — they need to distinguish “user without photo data” (silently ignored) from
“there was data, but the update failed” (counted and reported via mtrace() as a
failure, plus the picture_update_failed event). A previous version conflated the two
cases: the return value of update_picture_from_last_login() only indicated “there was
something to try”, not whether the attempt succeeded — which made the task log say
“completed” even when the download failed.
Scheduled task: retroactive photo backfill¶
Since the photo is only fetched during login (see above), users who authenticated before a
photo URL was available — or whose download failed at that time — remain without a photo
until their next login. The auth_suap\task\backfill_user_pictures class
(classes/task/backfill_user_pictures.php), registered in db/tasks.php, solves this
without requiring a new login: it reuses the SUAP payload already saved in
profile_field_last_login from each user’s last login.
Eligibility criteria¶
The task selects users with auth = 'suap', not deleted (deleted = 0) and without a
photo (picture = 0). For each one, it calls
auth_plugin_suap::get_last_login_photo_sources() (auth.php), which decodes the JSON
saved in profile_field_last_login and only considers the user eligible for download if
at least one of the expected photo attributes — url_foto_150x200, url_foto_75x100
or foto, in the same priority order used in create_or_update_user() — is present and
non-empty. Users without these attributes are simply skipped (this is not an error
condition). This method is shared with the bulk action described next and with the ad hoc
task described above.
The download and processing itself reuse auth_plugin_suap::update_picture() — the same
method used during login, with the same failure handling described above. Invalid JSON in
profile_field_last_login is also logged via debugging(...,
DEBUG_DEVELOPER), with the [AUTH SUAP] prefix.
How to run it¶
The task is registered disabled by default (runs only on demand). To run it through the web interface, whenever desired:
Go to Site administration → Server → Tasks → Scheduled tasks.
Find “SUAP: preencher fotos de usuários sem foto” (SUAP: fill photos for users without one).
Click Run now — available even with the task disabled; the output (
mtrace) is shown on the screen itself.
If you prefer recurring automatic execution, enable the task on that same screen; the default
schedule defined in db/tasks.php is daily, at 3 AM.
Bulk action: update photo from the user listing¶
Besides the scheduled task (which scans all users without a photo at once), it is possible to trigger the same update attempt for one or more specific users directly from the administrative listing, without waiting for the next task cycle.
auth/suap:updatepicture capability¶
Defined in db/access.php (captype write, system context), with default
CAP_ALLOW only for the manager archetype. Only those who have this capability in the
system context see the option in the bulk actions dropdown.
Where it appears¶
classes/hook_callbacks.php listens to the core hook
core_user\hook\extend_bulk_user_actions (registered in db/hooks.php) and adds the
“Atualizar foto (SUAP)” (Update photo (SUAP)) action to the plugin’s group in the
“With selected users…” menu, available both in Site administration → Users → Browse
list of users (admin/user.php, checking the per-row checkboxes) and in Bulk user
actions (admin/user/user_bulk.php).
Flow¶
Select one or more users (per-row checkbox) and choose “Atualizar foto (SUAP)” in the bulk actions dropdown.
updatepicture_bulk.php(plugin root) requires theauth/suap:updatepicturecapability and asks for confirmation, listing the names of the selected users.Upon confirmation, for each selected user it calls
auth_plugin_suap::queue_update_picture_task()— the same method used during login (see “Queueing (non-blocking)” above) — which queues an ad hoc task (auth_suap\task\update_user_picture_adhoc) instead of downloading the photo right away. This avoids the user administration screen freezing while the downloads happen; the actual processing occurs on the next cron cycle.At the end, it shows a summary: how many users had SUAP photo data and had the update scheduled in the background, and how many were skipped for not having that data saved (in that case, nothing is queued).
Unlike the scheduled task, this action does not filter by picture = 0 — it can also
be used to force a new attempt on users who already have a photo.
Scheduled task: retroactive name synchronization¶
Changing name_source_order/name_split_rule only affects users on their next login.
The auth_suap\task\sync_user_names class (classes/task/sync_user_names.php),
registered in db/tasks.php, retroactively applies the current rule to all existing users,
without requiring a new login — the same principle of reusing
profile_field_last_login already used by backfill_user_pictures.
Eligibility criteria¶
The task selects users with auth = 'suap', not deleted (deleted = 0). For each one, it
calls auth_plugin_suap::get_last_login_payload() to get the saved JSON; without a saved
payload, the user is skipped. With a payload, it computes
resolve_firstname_lastname() and only writes (via user_update_user()) if the result
differs from the current firstname/lastname.
How to run it¶
The task is registered disabled by default (runs only on demand), typically right after changing one of the two settings above:
Go to Site administration → Server → Tasks → Scheduled tasks.
Find “SUAP: sincronizar nomes de exibição dos usuários (nome social/usual/registro)” (SUAP: synchronize user display names (social/usual/registration name)).
Click Run now — available even with the task disabled; the output (
mtrace) is shown on the screen itself.
If you prefer recurring automatic execution, enable the task on that same screen; the default
schedule defined in db/tasks.php is daily, at 3 AM.
Notes¶
cpfandpassaporteare marked as deprecated in the code, but continue to be received and synchronized when SUAP returns them.profile_field_last_loginstores the raw JSON received from SUAP on every login — useful for troubleshooting synchronization issues, but should not be treated as a structured data source by other plugins.If
vinculo(object) is present in the combined payload, additional fields such asprofile_field_situacao_vinculo,profile_field_situacao_sistemica,profile_field_ira,profile_field_matriz_curricular,profile_field_ingresso_periodo,profile_field_curso_descricao,profile_field_turnoandprofile_field_campus_cursoare also filled in — seeauth.php::create_or_update_user()for the complete list and the precedence order betweenvinculos[](per-relationship detail equivalent tousername) andvinculo(current relationship).