['id' => int, 'name' => string] // Document type labels private const DOC_TYPE_LABELS = [ 'ART' => 'Article', 'COUV' => "Chapitre d'ouvrage", 'OUV' => 'Ouvrage', 'COMM' => 'Communication', 'ISSUE' => 'Direction de numéro', 'PROCEEDINGS' => 'Colloque', 'THESE' => 'Thèse', 'HDR' => 'HDR', 'SON' => 'Son', 'VIDEO' => 'Vidéo', ]; public function __construct() { $this->api = new Thalim_HAL_API(); } public function render() { if (!current_user_can('edit_others_posts')) { wp_die('Unauthorized'); } $this->handle_actions(); echo '

THALIM HAL Importer

'; $this->render_styles(); $this->render_message(); if (self::CONFIG_PANEL_ENABLED) { $this->render_config(); } $this->render_preview(); if (self::CSV_IMPORT_ENABLED) { $this->render_csv_import(); } echo '
'; } private function handle_actions() { if (!isset($_POST['thalim_hal_action'])) return; if (!wp_verify_nonce($_POST['_wpnonce'] ?? '', 'thalim_hal_action')) { $this->message = ['error', 'Security check failed.']; return; } $action = sanitize_text_field($_POST['thalim_hal_action']); if (self::CONFIG_PANEL_ENABLED && $action === 'test_api') { $this->handle_test_api(); } if ($action === 'refresh') { // Clear all preview transients (they are keyed by date range hash) global $wpdb; $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_thalim_hal_preview_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_thalim_hal_preview_%'"); $this->message = ['success', 'Preview data refreshed from HAL API.']; } if ($action === 'import_pending') { $this->handle_import(); } if (self::CSV_IMPORT_ENABLED) { if ($action === 'csv_upload') $this->handle_csv_upload(); if ($action === 'csv_batch') $this->handle_csv_batch(); if ($action === 'csv_cancel') $this->handle_csv_cancel(); if ($action === 'csv_download_report') $this->handle_csv_download_report(); } } /** * Handle bulk import of ready publications as pending posts. * Uses cached raw HAL docs to avoid a second outbound API call. */ private function handle_import() { $date_from = sanitize_text_field($_POST['hal_date_from'] ?? ''); $date_to = sanitize_text_field($_POST['hal_date_to'] ?? ''); $author_hal_id = sanitize_text_field($_POST['hal_author_id'] ?? ''); // Reuse the cached preview data — raw_docs are stored alongside processed docs $preview = $this->get_preview_data($date_from, $date_to, $author_hal_id); if (is_wp_error($preview)) { $this->message = ['error', 'API Error: ' . $preview->get_error_message()]; return; } $raw_docs = $preview['raw_docs'] ?? []; if (empty($raw_docs)) { $this->message = ['warning', 'Aucune publication dans le cache. Utilisez Filtrer pour charger les données d\'abord.']; return; } $this->load_wp_users_hal_ids(); $importer = new Thalim_HAL_Importer_Logic(); $imported = 0; $skipped = 0; $errors = []; foreach ($raw_docs as $doc) { $hal_id = $doc['halId_s'] ?? ''; $author_hal_ids = $doc['authIdHal_s'] ?? []; $matched_users = $this->match_authors_to_users($author_hal_ids); if (empty($matched_users) || $importer->is_imported($hal_id)) { $skipped++; continue; } $post_id = $importer->import($doc, $this->wp_users_by_hal_id); if (is_wp_error($post_id)) { $errors[] = $hal_id . ': ' . $post_id->get_error_message(); } else { $imported++; } } $msg = sprintf('%d publication(s) importée(s) en statut "En attente".', $imported); if ($skipped) $msg .= sprintf(' %d ignorée(s) (déjà importées ou sans membre THALIM correspondant).', $skipped); if (!empty($errors)) $msg .= ' Erreurs : ' . implode('; ', $errors); $this->message = [empty($errors) ? 'success' : 'warning', $msg]; } private function render_styles() { ?> message) return; printf('

%s

', esc_attr($this->message[0]), esc_html($this->message[1])); } private function render_preview() { // Read filters from POST (after submit) or GET (page reload with state) $date_from = sanitize_text_field($_POST['hal_date_from'] ?? $_GET['hal_date_from'] ?? ''); $date_to = sanitize_text_field($_POST['hal_date_to'] ?? $_GET['hal_date_to'] ?? ''); $author_hal_id = sanitize_text_field($_POST['hal_author_id'] ?? $_GET['hal_author_id'] ?? ''); // Users must be loaded before rendering the dropdown $this->load_wp_users_hal_ids(); $preview = $this->get_preview_data($date_from, $date_to, $author_hal_id); $ready_count = is_wp_error($preview) ? 0 : $preview['stats']['ready']; ?>

Import Preview

Mis en cache 5 min

get_error_message()); ?>

render_wp_users_debug(); ?> render_summary($preview['stats']); ?> render_preview_table($preview['docs']); ?> render_legend(); ?>
Total in HAL
Already Imported
Ready to Import
No Matched User
No publications found.

'; return; } ?>
Statut HAL ID Titre Type Auteurs IDs HAL auteurs Date Membres THALIM Lien HAL
get_status_icon($doc); ?>
wp_users_by_hal_id[$normalized]); ?> aucun Aucun Voir sur HAL
Légende : ✓ Importé ★ Prêt Membre THALIM identifié ✗ Bloqué Aucun membre THALIM ne correspond aux IDs auteurs HAL
load_wp_users_hal_ids(); if (empty($this->wp_users_by_hal_id)) { echo '

Aucun utilisateur WordPress n\'a le champ identifiant_hal renseigné.

'; return; } ?>
Utilisateurs WordPress avec identifiant HAL (wp_users_by_hal_id); ?> utilisateurs) — Cliquer pour déplier wp_users_by_hal_id as $hal_id => $user): ?>
UtilisateurIdentifiant HALDebug (brut)Modifier
(ID : ) "" ( car.) Modifier
api->fetch_publications($rows, 0, 'producedDate_tdate desc', $date_from, $date_to, $author_hal_id); if (is_wp_error($result)) return $result; $importer = new Thalim_HAL_Importer_Logic(); $this->load_wp_users_hal_ids(); $preview = [ 'stats' => [ 'total' => $result['response']['numFound'] ?? 0, 'imported' => 0, 'ready' => 0, 'blocked' => 0 ], 'docs' => [], 'raw_docs' => [], // Raw HAL docs kept for import, avoids a second API call ]; foreach ($result['response']['docs'] ?? [] as $doc) { $hal_id = $doc['halId_s'] ?? ''; $is_imported = $importer->is_imported($hal_id); $author_hal_ids = $doc['authIdHal_s'] ?? []; $matched_users = $this->match_authors_to_users($author_hal_ids); $has_match = !empty($matched_users); // Update stats if ($is_imported) { $preview['stats']['imported']++; } elseif ($has_match) { $preview['stats']['ready']++; } else { $preview['stats']['blocked']++; } $preview['docs'][] = [ 'hal_id' => $hal_id, 'title' => $doc['title_s'][0] ?? 'N/A', 'type' => $doc['docType_s'] ?? '', 'authors' => $doc['authFullName_s'] ?? [], 'author_hal_ids' => $author_hal_ids, 'publication_date' => $doc['publicationDate_s'] ?? '', 'produced_date' => $doc['submittedDate_s'] ?? '', 'journal' => $doc['journalTitle_s'] ?? $doc['bookTitle_s'] ?? '', 'url' => $doc['uri_s'] ?? '', 'is_imported' => $is_imported, 'matched_users' => $matched_users, 'has_match' => $has_match, ]; $preview['raw_docs'][] = $doc; // Full HAL doc kept for import } set_transient($cache_key, $preview, 300); return $preview; } /** * Load all WordPress users with HAL IDs into cache. * Stores: normalized_hal_id => ['id' => int, 'name' => string] */ private function load_wp_users_hal_ids() { if ($this->wp_users_by_hal_id !== null) return; $this->wp_users_by_hal_id = []; $users = get_users([ 'meta_key' => 'identifiant_hal', 'meta_compare' => 'EXISTS' ]); foreach ($users as $user) { $hal_id = get_user_meta($user->ID, 'identifiant_hal', true); if (!empty($hal_id)) { $normalized = strtolower(trim($hal_id)); $this->wp_users_by_hal_id[$normalized] = [ 'id' => $user->ID, 'name' => $user->display_name, 'hal_id' => trim($hal_id), // original value for API filter ]; } } } /** * Match HAL author IDs to WordPress users. * Returns array of display names (for preview display). */ private function match_authors_to_users($author_hal_ids) { $matched = []; foreach ($author_hal_ids as $hal_id) { $normalized = strtolower(trim($hal_id)); if (isset($this->wp_users_by_hal_id[$normalized])) { $matched[] = $this->wp_users_by_hal_id[$normalized]['name']; } } return $matched; } private function get_row_class($doc) { if ($doc['is_imported']) return 'hal-status-imported'; if ($doc['has_match']) return 'hal-status-ready'; return 'hal-status-blocked'; } private function get_status_icon($doc) { if ($doc['is_imported']) return ''; if ($doc['has_match']) return ''; return ''; } }