Import HAL : match des auteurs sur toutes les formes d'idHAL (résolution en fond via cron)
This commit is contained in:
115
includes/class-hal-forms-cache.php
Normal file
115
includes/class-hal-forms-cache.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Background resolver + cache for alternate idHAL forms.
|
||||
*
|
||||
* Problem: a member's WP `identifiant_hal` stores ONE idHAL form, but HAL may
|
||||
* tag their publications under other valid forms of the same person. Matching
|
||||
* on the single stored form silently drops those links.
|
||||
*
|
||||
* Fix: resolve every member's full set of valid idHAL forms (via HAL ref/author)
|
||||
* and cache them in usermeta `_hal_id_forms`. Matching then uses the cached
|
||||
* forms — with ZERO network calls in the UI / import path.
|
||||
*
|
||||
* All resolution happens in the background via WP-Cron, in small batches, so the
|
||||
* admin interface and the import flow stay as fast as before.
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Thalim_HAL_Forms_Cache {
|
||||
|
||||
const META_FORMS = '_hal_id_forms'; // usermeta: JSON array of forms
|
||||
const META_SOURCE = '_hal_id_forms_src'; // usermeta: the identifiant_hal the cache was built from
|
||||
const CRON_HOOK = 'thalim_hal_refresh_forms';
|
||||
const BATCH_SIZE = 15; // members resolved per cron tick
|
||||
|
||||
/** Wire the background hooks. Called once at plugins_loaded. */
|
||||
public static function init() {
|
||||
add_action(self::CRON_HOOK, [__CLASS__, 'run_batch']);
|
||||
|
||||
// Ensure the recurring event exists (hourly).
|
||||
if (!wp_next_scheduled(self::CRON_HOOK)) {
|
||||
wp_schedule_event(time() + 60, 'hourly', self::CRON_HOOK);
|
||||
}
|
||||
|
||||
// When a profile's identifiant_hal changes, invalidate its cache so the
|
||||
// next background tick re-resolves it (no synchronous API call here).
|
||||
add_action('updated_user_meta', [__CLASS__, 'on_user_meta_change'], 10, 4);
|
||||
add_action('added_user_meta', [__CLASS__, 'on_user_meta_change'], 10, 4);
|
||||
}
|
||||
|
||||
/** Remove the scheduled event (plugin deactivation). */
|
||||
public static function clear_schedule() {
|
||||
$ts = wp_next_scheduled(self::CRON_HOOK);
|
||||
if ($ts) wp_unschedule_event($ts, self::CRON_HOOK);
|
||||
}
|
||||
|
||||
/** Invalidate a user's cache when their identifiant_hal is edited. */
|
||||
public static function on_user_meta_change($meta_id, $user_id, $meta_key, $_meta_value) {
|
||||
if ($meta_key === 'identifiant_hal') {
|
||||
delete_user_meta($user_id, self::META_SOURCE); // marks the cache stale
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read cached alternate forms for a user's identifiant_hal.
|
||||
* Always returns at least the stored form itself (graceful degradation:
|
||||
* behaves exactly like the old code until the background job has run).
|
||||
*
|
||||
* @return string[] lowercased idHAL forms.
|
||||
*/
|
||||
public static function forms_for_user($user_id, $identifiant_hal) {
|
||||
$base = strtolower(trim((string) $identifiant_hal));
|
||||
if ($base === '') return [];
|
||||
|
||||
$src = get_user_meta($user_id, self::META_SOURCE, true);
|
||||
if ($src === $base) {
|
||||
$cached = json_decode((string) get_user_meta($user_id, self::META_FORMS, true), true);
|
||||
if (is_array($cached) && $cached) {
|
||||
$cached[] = $base;
|
||||
return array_values(array_unique(array_map('strtolower', $cached)));
|
||||
}
|
||||
}
|
||||
// Not resolved yet (or stale) -> fall back to the single stored form.
|
||||
return [$base];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron worker: resolve forms for a batch of members whose cache is missing
|
||||
* or stale. Runs in the background; safe to be interrupted (idempotent).
|
||||
*/
|
||||
public static function run_batch() {
|
||||
$users = get_users([
|
||||
'meta_key' => 'identifiant_hal',
|
||||
'meta_compare' => 'EXISTS',
|
||||
'fields' => ['ID'],
|
||||
'number' => 500,
|
||||
]);
|
||||
|
||||
// Select those needing (re)resolution: stored form != cached source.
|
||||
$todo = [];
|
||||
foreach ($users as $u) {
|
||||
$idhal = strtolower(trim((string) get_user_meta($u->ID, 'identifiant_hal', true)));
|
||||
if ($idhal === '') continue;
|
||||
$src = get_user_meta($u->ID, self::META_SOURCE, true);
|
||||
if ($src !== $idhal) {
|
||||
$todo[$idhal][] = (int) $u->ID; // group users sharing a slug
|
||||
}
|
||||
if (count($todo) >= self::BATCH_SIZE) break;
|
||||
}
|
||||
if (empty($todo)) return;
|
||||
|
||||
$api = new Thalim_HAL_API();
|
||||
$expanded = $api->expand_hal_id_forms(array_keys($todo));
|
||||
|
||||
foreach ($todo as $idhal => $user_ids) {
|
||||
$forms = $expanded[$idhal] ?? [$idhal];
|
||||
foreach ($user_ids as $uid) {
|
||||
update_user_meta($uid, self::META_FORMS, wp_json_encode(array_values($forms)));
|
||||
update_user_meta($uid, self::META_SOURCE, $idhal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user