variants to \n and strips any remaining HTML tags. */ function thalim_sanitize_domaines( $raw ) { // Normalise all
variants (including \r before them) to a newline $text = preg_replace( '/\r?/i', "\n", $raw ); // Strip any remaining HTML tags $text = strip_tags( $text ); // Clean up excess blank lines / whitespace $text = preg_replace( "/\n{3,}/", "\n\n", trim( $text ) ); return $text; } /** * Build the display data array for a single user. */ function thalim_build_membre_data( $user ) { $lang = thalim_current_language(); $status_parts = []; $role_names = []; for ( $n = 1; $n <= 3; $n++ ) { $role_id = get_user_meta( $user->ID, 'role_' . $n, true ); if ( ! $role_id ) continue; $term = get_term( intval( $role_id ), 'role' ); if ( ! $term || is_wp_error( $term ) ) continue; $role_names[] = $term->name; $override = thalim_bilingual( get_user_meta( $user->ID, 'affichage_du_statut_' . $n, true ) ?: '', $lang ); if ( $override ) { $status_parts[] = $override; } else { $entry = $term->name; $complement = thalim_bilingual( get_user_meta( $user->ID, 'complement_de_role_' . $n, true ) ?: '', $lang ); if ( $complement ) $entry .= ' ' . $complement; $status_parts[] = $entry; } } // Avatar (Simple Local Avatar with Gravatar fallback) $avatar_url = thalim_get_user_avatar_url( $user->ID ); // Domaines de recherches: multiple usermeta rows, each is a post_tag term ID $domaine_ids = get_user_meta( $user->ID, 'domaines_de_recherches', false ); $domaines = []; foreach ( $domaine_ids as $term_id ) { $term = get_term( intval( $term_id ), 'post_tag' ); if ( $term && ! is_wp_error( $term ) ) { $domaines[] = $term->name; } } return [ 'display_name' => $user->display_name, 'sort_key' => thalim_get_sort_key( $user->ID, $user->display_name ), 'url' => get_author_posts_url( $user->ID ), 'status' => implode( ', ', $status_parts ), 'affiliation' => (function() use ($user) { $v = get_user_meta( $user->ID, 'affiliation', true ) ?: ''; return strtolower( $v ) === 'autre' ? ( get_user_meta( $user->ID, 'affiliation_autre', true ) ?: '' ) : $v; })(), 'role_names' => $role_names, 'avatar_url' => $avatar_url, 'domaines' => $domaines, 'autres_domaines' => thalim_sanitize_domaines( get_user_meta( $user->ID, 'autres_domaines_de_recherches', true ) ?: '' ), ]; } /** * Return all role taxonomy terms that are in use, sorted by name. */ function thalim_get_role_terms() { $terms = get_terms( [ 'taxonomy' => 'role', 'hide_empty' => true, 'orderby' => 'name' ] ); if ( is_wp_error( $terms ) ) return []; return array_values( array_map( fn( $t ) => [ 'id' => $t->term_id, 'name' => $t->name ], array_filter( $terms, fn( $t ) => ! in_array( mb_strtolower( $t->name, 'UTF-8' ), [ 'archive', 'à ranger' ], true ) ) ) ); } /** * Return all role term_ids set for a user (role_1, role_2, role_3). */ function thalim_get_user_role_ids( $user_id ) { $ids = []; for ( $n = 1; $n <= 3; $n++ ) { $role_id = get_user_meta( $user_id, 'role_' . $n, true ); if ( $role_id ) $ids[] = intval( $role_id ); } return $ids; } /** * Sort key: first word of last_name user meta (handles compound last names like * "Duclaux de l'Estoile" → "Duclaux"). Falls back to last word of display_name. */ function thalim_get_sort_key( $user_id, $display_name ) { $last = get_user_meta( $user_id, 'last_name', true ); if ( $last ) { $parts = explode( ' ', trim( $last ) ); return $parts[0]; } $parts = explode( ' ', trim( $display_name ) ); return end( $parts ); } /** * Return all member groups for the /membres page. * Each group: ['title' => string, 'members' => array of member data arrays]. * Empty groups are omitted. */ function thalim_get_membres_groups() { // Fetch all users that have role_1 set $users = get_users( [ 'meta_key' => 'role_1', 'number' => -1, ] ); // Direction: read directeur and directeur_adjoint from "Le laboratoire" page $labo_page = get_page_by_path( 'le-laboratoire' ); $directeur_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur', true ) ) : 0; $adjoint_id = $labo_page ? intval( get_post_meta( $labo_page->ID, 'directeur_adjoint', true ) ) : 0; $direction_users = []; foreach ( [ $directeur_id, $adjoint_id ] as $uid ) { if ( $uid ) { $u = get_userdata( $uid ); if ( $u ) $direction_users[] = $u; } } // Pre-build member data for all relevant users (cache by ID) $member_cache = []; $all_users = array_merge( $users, $direction_users ); foreach ( $all_users as $user ) { if ( ! isset( $member_cache[ $user->ID ] ) ) { $member_cache[ $user->ID ] = thalim_build_membre_data( $user ); } } // Prepend direction title to status for director / deputy director if ( $directeur_id && isset( $member_cache[ $directeur_id ] ) ) { $existing = $member_cache[ $directeur_id ]['status']; $member_cache[ $directeur_id ]['status'] = 'Directeur' . ( $existing ? ', ' . $existing : '' ); } if ( $adjoint_id && isset( $member_cache[ $adjoint_id ] ) ) { $existing = $member_cache[ $adjoint_id ]['status']; $member_cache[ $adjoint_id ]['status'] = 'Directeur adjoint' . ( $existing ? ', ' . $existing : '' ); } // Build a slug→ID map for the 'role' taxonomy so group definitions survive // database migrations where auto-incremented term IDs change. $slug_to_id = []; foreach ( get_terms( [ 'taxonomy' => 'role', 'hide_empty' => false ] ) as $term ) { $slug_to_id[ $term->slug ] = $term->term_id; } $by_slug = fn( ...$slugs ) => array_values( array_filter( array_map( fn( $s ) => $slug_to_id[ $s ] ?? null, $slugs ) ) ); // Group definitions (title => role slugs that qualify a user for membership) $group_definitions = [ 'Chercheuses et chercheurs CNRS' => $by_slug( 'directeur-de-recherche', 'charge-de-recherche' ), 'Enseignantes-chercheuses et enseignants-chercheurs' => $by_slug( 'professeur', 'maitre-de-conferences' ), 'Doctorantes et doctorants' => $by_slug( 'doctorant' ), 'Docteures et docteurs' => $by_slug( 'docteur' ), 'Postdoctorantes et postdoctorants' => $by_slug( 'postdoctorant' ), 'Personnel contractuel' => $by_slug( 'personnel-contractuel' ), "Personnel d'accompagnement à la recherche" => $by_slug( 'personnel-technique' ), 'Membres associées et membres associés' => $by_slug( 'membre-associe' ), 'Anciennes et anciens membres' => $by_slug( 'anciens-membres' ), ]; $groups = []; // Direction group first: directeur before directeur adjoint $direction_members = []; if ( $directeur_id && isset( $member_cache[ $directeur_id ] ) ) { $direction_members[] = $member_cache[ $directeur_id ]; } if ( $adjoint_id && isset( $member_cache[ $adjoint_id ] ) ) { $direction_members[] = $member_cache[ $adjoint_id ]; } if ( ! empty( $direction_members ) ) { $groups[] = [ 'title' => 'Direction', 'members' => $direction_members, 'fixed_order' => true, ]; } // Role-based groups (a user appears in every group that matches any of their roles) foreach ( $group_definitions as $title => $term_ids ) { $group_members = []; foreach ( $users as $user ) { $user_role_ids = thalim_get_user_role_ids( $user->ID ); if ( array_intersect( $term_ids, $user_role_ids ) ) { $group_members[] = $member_cache[ $user->ID ]; } } if ( empty( $group_members ) ) continue; // Sort alphabetically by last name, accent- and case-insensitive (fr locale) static $collator = null; if ( $collator === null ) { $collator = class_exists( 'Collator' ) ? new Collator( 'fr_FR' ) : false; if ( $collator ) $collator->setStrength( Collator::PRIMARY ); } usort( $group_members, function( $a, $b ) use ( $collator ) { $la = $a['sort_key']; $lb = $b['sort_key']; if ( $collator ) return $collator->compare( $la, $lb ); return strcmp( mb_strtolower( $la, 'UTF-8' ), mb_strtolower( $lb, 'UTF-8' ) ); } ); // In "Personnel d'accompagnement", place "Gestion et pilotage" first $fixed = false; if ( $title === "Personnel d'accompagnement à la recherche" ) { $priority = []; $rest = []; foreach ( $group_members as $m ) { if ( stripos( $m['status'], 'Gestion et pilotage' ) !== false ) { $priority[] = $m; } else { $rest[] = $m; } } $group_members = array_merge( $priority, $rest ); $fixed = true; } $groups[] = [ 'title' => $title, 'members' => $group_members, 'fixed_order' => $fixed, ]; } return $groups; }