Files
thalim-theme/inc/single-helpers.php

477 lines
21 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Format a Pods datetime string (e.g. "2026-01-17 00:00:00") via date_i18n.
* Default format is "j F Y" (full month name, FR/EN per WP locale).
* Returns empty string for null/empty/"0000-00-00..." inputs.
*/
function thalim_format_date($raw, $lang = 'fr', $format = 'j F Y') {
if (!$raw || str_starts_with($raw, '0000-00-00')) return '';
$ts = strtotime($raw);
if ($ts === false || $ts < 0) return '';
return date_i18n($format, $ts);
}
/**
* Tell whether an image is displayed in portrait, taking EXIF orientation
* into account. WordPress stores the raw (un-rotated) width/height, but the
* browser auto-rotates JPEGs that carry an EXIF orientation flag of 58
* (90°/270° rotation), which swaps the displayed dimensions. Without this,
* a phone photo shot in portrait can be mis-classified as landscape.
*/
function thalim_image_is_portrait($doc_id, $w, $h) {
$path = get_attached_file($doc_id);
if ($path && function_exists('exif_read_data') && preg_match('/\.jpe?g$/i', $path)) {
$exif = @exif_read_data($path);
$orientation = isset($exif['Orientation']) ? (int) $exif['Orientation'] : 0;
if (in_array($orientation, [5, 6, 7, 8], true)) {
[$w, $h] = [$h, $w];
}
}
return $h > $w;
}
/**
* Resolve all Pods custom fields for a single post into a display-ready array.
*/
function thalim_get_single_data($post_id) {
$lang = thalim_current_language();
$data = [
// Text fields
'sous_titre' => thalim_bilingual( get_post_meta($post_id, 'sous-titre', true) ?: '', $lang ),
'reference_bibliographique' => get_post_meta($post_id, 'reference_bibliographique', true) ?: '',
'editeur' => get_post_meta($post_id, 'editeur', true) ?: '',
'journal' => get_post_meta($post_id, 'journal', true) ?: '',
'lieu' => thalim_bilingual( get_post_meta($post_id, 'lieu', true) ?: '', $lang ),
'adresse' => get_post_meta($post_id, 'adresse', true) ?: '',
'autrepersonnes' => get_post_meta($post_id, 'autrepersonnes', true) ?: '',
'autre_autrepersonnes' => get_post_meta($post_id, 'autre_autrepersonnes', true) ?: '',
'body_en' => apply_filters( 'the_content', get_post_meta($post_id, 'body_en', true) ?: '' ),
// Dates (formatted for display)
'datetime' => thalim_format_date(get_post_meta($post_id, 'datetime', true), $lang),
'date_de_debut' => '',
'date_de_fin' => '',
'date_debut_ymd' => '',
'date_fin_ymd' => '',
'heure_de_debut' => substr( get_post_meta($post_id, 'heure_de_debut', true) ?: '', 0, 5 ),
'heure_de_fin' => substr( get_post_meta($post_id, 'heure_de_fin', true) ?: '', 0, 5 ),
// URLs
'hal_url' => get_post_meta($post_id, 'hal_url', true) ?: '',
'hal_file' => get_post_meta($post_id, 'hal_file', true) ?: '',
'canal_u' => array_values( array_filter( array_map( function( $url ) {
if ( preg_match( '/(\d+)\/?$/', trim( $url ), $m ) ) {
return 'https://www.canal-u.tv/embed/' . $m[1] . '?t=0';
}
return '';
}, get_post_meta( $post_id, 'lien_canal_u', false ) ) ) ),
'youtube' => array_values( array_filter( array_map( function( $url ) {
$url = trim( $url );
// youtu.be/ID or youtube.com/embed/ID or youtube.com/watch?v=ID
if ( preg_match( '/(?:youtu\.be\/|youtube\.com\/(?:embed\/|watch\?.*v=|shorts\/))([A-Za-z0-9_-]{11})/', $url, $m ) ) {
return 'https://www.youtube-nocookie.com/embed/' . $m[1];
}
return '';
}, get_post_meta( $post_id, 'lien_youtube', false ) ) ) ),
// Resolved below
'liens_externes' => [],
'membres' => [],
'autre_membres' => [],
'autre_fonction_label' => '',
'axes' => [],
'etiquettes' => [],
'programmes' => [],
'annonces_liees' => [],
'seances_a_venir' => [],
'seances_passees' => [],
'show_image_titles' => (bool) get_post_meta($post_id, 'afficher_le_titre_des_images_en_legende', true),
'images' => [],
'documents' => [],
'type_label' => '',
'fonction_label' => '',
'parent_slug' => '',
'parent_name' => '',
'parent_link' => '',
'category_name' => '',
'category_link' => '',
];
// --- Dates ---
$raw_debut = get_post_meta($post_id, 'date_de_debut', true);
$raw_fin = get_post_meta($post_id, 'date_de_fin', true);
$ts_debut = ($raw_debut && !str_starts_with($raw_debut, '0000-00-00')) ? strtotime($raw_debut) : 0;
$ts_fin = ($raw_fin && !str_starts_with($raw_fin, '0000-00-00')) ? strtotime($raw_fin) : 0;
$data['date_de_debut'] = thalim_format_date($raw_debut, $lang);
$data['date_de_fin'] = thalim_format_date($raw_fin, $lang);
if ($ts_debut) $data['date_debut_ymd'] = date('Y-m-d', $ts_debut);
if ($ts_fin) $data['date_fin_ymd'] = date('Y-m-d', $ts_fin);
// Ouvrages (cat 15): override display to year only — raw timestamps and
// *_ymd fields stay full-precision so sorting/filtering on index pages
// (`thalim_event_date_order`) keeps working.
if (in_array(15, wp_get_post_categories($post_id), true)) {
$data['date_de_debut'] = thalim_format_date($raw_debut, $lang, 'Y');
$data['date_de_fin'] = thalim_format_date($raw_fin, $lang, 'Y');
$data['datetime'] = thalim_format_date(get_post_meta($post_id, 'datetime', true), $lang, 'Y');
}
// --- External links (up to 3) ---
for ($i = 1; $i <= 3; $i++) {
$url = get_post_meta($post_id, 'lien_externe_' . $i, true);
if ($url) {
$titre = thalim_bilingual( get_post_meta($post_id, 'titre_du_lien_externe_' . $i, true) ?: '', $lang );
if (!$titre) {
$host = parse_url($url, PHP_URL_HOST) ?: $url;
$parts = explode('.', $host);
$titre = count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host;
}
$data['liens_externes'][] = [
'url' => $url,
'titre' => $titre,
];
}
}
// --- Category hierarchy for breadcrumb and color ---
$categories = wp_get_post_categories($post_id, ['fields' => 'all']);
$excluded_ids = [12, 31];
foreach ($categories as $cat) {
if (in_array($cat->term_id, $excluded_ids)) continue;
$ancestor_ids = get_ancestors($cat->term_id, 'category');
if (!empty($ancestor_ids)) {
$root = get_category(end($ancestor_ids));
$data['parent_slug'] = thalim_category_color_slug($root->term_id, $root->slug);
$data['parent_name'] = $root->name;
$data['parent_link'] = get_category_link($root->term_id);
$data['category_name'] = $cat->name;
} else {
$data['parent_slug'] = thalim_category_color_slug($cat->term_id, $cat->slug);
$data['parent_name'] = $cat->name;
$data['parent_link'] = get_category_link($cat->term_id);
$data['category_name'] = $lang === 'en' ? 'Other' : 'Autre';
}
// category_link: for direct posts (no ancestors), point to the /autres index
$data['category_link'] = empty($ancestor_ids)
? trailingslashit(get_category_link($cat->term_id)) . 'autres/'
: get_category_link($cat->term_id);
break;
}
// --- Documents joints: split images vs files ---
$doc_ids = get_post_meta($post_id, 'documents_joints', false);
foreach ($doc_ids as $doc_id) {
$mime = get_post_mime_type($doc_id);
if (!$mime) continue;
if (str_starts_with($mime, 'image/')) {
$src = wp_get_attachment_image_url($doc_id, 'large');
if ($src) {
$meta = wp_get_attachment_metadata($doc_id);
$w = isset($meta['width']) ? $meta['width'] : 0;
$h = isset($meta['height']) ? $meta['height'] : 0;
$data['images'][] = [
'url' => $src,
'alt' => get_post_meta($doc_id, '_wp_attachment_image_alt', true) ?: '',
'caption' => thalim_bilingual(wp_get_attachment_caption($doc_id) ?: '', $lang),
'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang),
'portrait' => thalim_image_is_portrait($doc_id, $w, $h),
];
}
} else {
$data['documents'][] = [
'url' => wp_get_attachment_url($doc_id),
'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang) ?: basename(get_attached_file($doc_id)),
];
}
}
// --- Members (user IDs → name + profile URL) ---
foreach (get_post_meta($post_id, 'membres', false) as $uid) {
$user = get_userdata($uid);
if ($user) {
$data['membres'][] = [
'name' => $user->display_name,
'url' => get_author_posts_url($uid),
];
}
}
// --- Autre membres (user IDs → name + profile URL) ---
foreach (get_post_meta($post_id, 'autre_membres', false) as $uid) {
$user = get_userdata($uid);
if ($user) {
$data['autre_membres'][] = [
'name' => $user->display_name,
'url' => get_author_posts_url($uid),
];
}
}
// --- Axes thématiques (taxonomy term IDs) ---
$axe_ids = get_post_meta($post_id, 'axes_thematiques', false);
foreach ($axe_ids as $axe_id) {
$term = get_term(intval($axe_id), 'axe_thematique');
if ($term && !is_wp_error($term)) {
$data['axes'][] = [
'id' => $term->term_id,
'name' => thalim_bilingual($term->name, $lang),
'url' => get_term_link($term),
];
}
}
// --- Étiquettes (taxonomy term IDs) ---
$tag_ids = get_post_meta($post_id, 'etiquettes', false);
foreach ($tag_ids as $tag_id) {
$term = get_term(intval($tag_id), 'post_tag');
if ($term && !is_wp_error($term)) {
$data['etiquettes'][] = [
'id' => $term->term_id,
'name' => thalim_bilingual($term->name, $lang),
'url' => get_term_link($term),
];
}
}
// --- Programmes de recherche (taxonomy term IDs) ---
$prog_ids = get_post_meta($post_id, 'programmes_de_recherche', false);
foreach ($prog_ids as $prog_id) {
$term = get_term(intval($prog_id), 'programme_de_recherche');
if ($term && !is_wp_error($term)) {
$data['programmes'][] = [
'id' => $term->term_id,
'name' => thalim_bilingual($term->name, $lang),
'url' => get_term_link($term),
];
}
}
// --- Annonces liées (related posts) ---
$related_ids = get_post_meta($post_id, 'annonces_liees', false);
if (!empty($related_ids)) {
$data['annonces_liees'] = Timber::get_posts([
'post_type' => 'post',
'post__in' => array_map('intval', $related_ids),
'posts_per_page' => -1,
'lang' => '',
]);
}
// --- Séances (session posts) — split into upcoming / past ---
$seance_ids = get_post_meta($post_id, 'seances', false);
$data['seances_a_venir'] = [];
$data['seances_passees'] = [];
if (!empty($seance_ids)) {
$seance_posts = Timber::get_posts([
'post_type' => 'post',
'post__in' => array_map('intval', $seance_ids),
'posts_per_page' => -1,
'orderby' => 'meta_value',
'meta_key' => 'date_de_debut',
'order' => 'ASC',
'lang' => '',
'post_status' => ['publish', 'future'],
]);
$now = time();
$current_year = date('Y');
$months_fr = ['jan.', 'fév.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'];
$months_en = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'];
foreach ($seance_posts as $seance) {
$raw_date = get_post_meta($seance->ID, 'date_de_debut', true);
$ts = $raw_date ? strtotime($raw_date) : strtotime($seance->post_date);
// Only expose date_fin when it's a different day than date_de_debut
$raw_fin = get_post_meta($seance->ID, 'date_de_fin', true);
$ts_fin = $raw_fin && !str_starts_with($raw_fin, '0000-00-00') ? strtotime($raw_fin) : false;
$date_fin_display = ($ts_fin && date('Y-m-d', $ts_fin) !== date('Y-m-d', $ts))
? thalim_format_date($raw_fin, $lang)
: '';
$month_idx = intval(date('n', $ts)) - 1;
$seance_data = [
'post' => $seance,
'day' => date('d', $ts),
'month' => ($lang === 'en') ? $months_en[$month_idx] : $months_fr[$month_idx],
'year' => (date('Y', $ts) !== $current_year) ? date('Y', $ts) : '',
'date_full' => thalim_format_date($raw_date, $lang),
'date_fin' => $date_fin_display,
'heure_de_debut' => substr( get_post_meta($seance->ID, 'heure_de_debut', true) ?: '', 0, 5 ),
'heure_de_fin' => substr( get_post_meta($seance->ID, 'heure_de_fin', true) ?: '', 0, 5 ),
'lieu' => thalim_bilingual( get_post_meta($seance->ID, 'lieu', true) ?: '', $lang ),
'adresse' => get_post_meta($seance->ID, 'adresse', true) ?: '',
'body_en' => apply_filters( 'the_content', get_post_meta($seance->ID, 'body_en', true) ?: '' ),
'intervenants' => [],
'images' => [],
'documents' => [],
'liens_externes' => [],
'annonces_liees' => [],
];
// Resolve intervenants (membres or autrepersonnes)
$m_ids = get_post_meta($seance->ID, 'membres', false);
if (empty($m_ids)) {
$m_ids = get_post_meta($seance->ID, 'autre_membres', false);
}
foreach ($m_ids as $uid) {
$user = get_userdata($uid);
if ($user) {
$seance_data['intervenants'][] = [
'name' => $user->display_name,
'url' => get_author_posts_url($uid),
];
}
}
$seance_data['autrepersonnes'] = get_post_meta($seance->ID, 'autrepersonnes', true) ?: '';
$seance_data['show_image_titles'] = (bool) get_post_meta($seance->ID, 'afficher_le_titre_des_images_en_legende', true);
// Documents joints: images and files
$s_doc_ids = get_post_meta($seance->ID, 'documents_joints', false);
foreach ($s_doc_ids as $doc_id) {
$mime = get_post_mime_type($doc_id);
if (!$mime) continue;
if (str_starts_with($mime, 'image/')) {
$src = wp_get_attachment_image_url($doc_id, 'large');
if ($src) {
$seance_data['images'][] = [
'url' => $src,
'alt' => get_post_meta($doc_id, '_wp_attachment_image_alt', true) ?: '',
'caption' => thalim_bilingual(wp_get_attachment_caption($doc_id) ?: '', $lang),
'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang),
];
}
} else {
$seance_data['documents'][] = [
'url' => wp_get_attachment_url($doc_id),
'title' => thalim_bilingual(get_the_title($doc_id) ?: '', $lang) ?: basename(get_attached_file($doc_id)),
];
}
}
// External links (up to 3)
for ($i = 1; $i <= 3; $i++) {
$url = get_post_meta($seance->ID, 'lien_externe_' . $i, true);
if ($url) {
$titre = thalim_bilingual( get_post_meta($seance->ID, 'titre_du_lien_externe_' . $i, true) ?: '', $lang );
if (!$titre) {
$host = parse_url($url, PHP_URL_HOST) ?: $url;
$parts = explode('.', $host);
$titre = count($parts) >= 2 ? implode('.', array_slice($parts, -2)) : $host;
}
$seance_data['liens_externes'][] = ['url' => $url, 'titre' => $titre];
}
}
// Annonces liées
$s_related_ids = get_post_meta($seance->ID, 'annonces_liees', false);
if (!empty($s_related_ids)) {
$seance_data['annonces_liees'] = Timber::get_posts([
'post_type' => 'post',
'post__in' => array_map('intval', $s_related_ids),
'posts_per_page' => -1,
'lang' => '',
]);
}
if ($ts >= $now) {
$data['seances_a_venir'][] = $seance_data;
} else {
$data['seances_passees'][] = $seance_data;
}
}
// Past séances: most recent first
$data['seances_passees'] = array_reverse($data['seances_passees']);
}
// --- Type label (category-conditional type fields) ---
$type_fields = [
'type_colloque_journee_d_etude',
'type_soutenance',
'type_evenement_culturel',
'type_media',
'type_captation',
'type_revue_collection',
'type_autre',
];
foreach ($type_fields as $field) {
$val = get_post_meta($post_id, $field, true);
if ($val) {
$data['type_label'] = thalim_bilingual( $val, $lang );
break;
}
}
// --- Fonction label (first non-empty fonction_* field) ---
$fonction_fields = [
'fonction_auteur',
'fonction_organisation',
'fonction_intervention',
'fonction_redaction',
'fonction_realisation',
'fonction_dirige',
'fonction_responsable',
'fonction_candidat',
];
foreach ($fonction_fields as $field) {
$val = get_post_meta($post_id, $field, true);
if ($val) {
$data['fonction_label'] = thalim_bilingual( $val, $lang );
break;
}
}
// --- Autre fonction label (first non-empty autre_fonction_* field) ---
$autre_fonction_fields = [
'autre_fonction_autre',
'autre_fonction_concerne',
'autre_fonction_directeur',
'autre_fonction_direction_d_ouvrage',
'autre_fonction_intervenant',
'autre_fonction_participants',
];
foreach ($autre_fonction_fields as $field) {
$val = get_post_meta($post_id, $field, true);
if ($val) {
$data['autre_fonction_label'] = thalim_bilingual( $val, $lang );
break;
}
}
// --- Fallback: derive labels from Pods categorie ID for older posts ---
if (!$data['fonction_label'] || !$data['autre_fonction_label']) {
$pods_cat = get_post_meta($post_id, '_pods_categorie', true);
$cat_id = (is_array($pods_cat) && !empty($pods_cat)) ? intval($pods_cat[0]) : 0;
// Pods categorie ID → fonction label (main membres)
$cat_to_fonction = [
3 => 'Organisation', 4 => 'Auteur', 6 => 'Responsable',
8 => 'Organisation', 9 => 'Responsable', 10 => 'Organisation',
11 => 'Organisation', 12 => 'Intervention', 13 => 'Intervention',
14 => 'Candidat', 15 => 'Auteur', 16 => 'Auteur',
17 => 'Responsable', 18 => 'Organisation', 19 => 'Intervention',
21 => 'Rédaction', 22 => 'Réalisation', 23 => 'Intervention',
24 => 'Responsable', 25 => 'Responsable', 65 => 'Dirigé par',
];
// Pods categorie ID → autre_fonction label (autre membres)
$cat_to_autre_fonction = [
3 => 'Participants', 4 => "Direction d'ouvrage",
10 => 'Participants', 14 => 'Directeur de thèse',
15 => "Direction d'ouvrage", 16 => "Direction d'ouvrage",
19 => 'Membre concerné', 22 => 'Intervenant',
];
if (!$data['fonction_label'] && isset($cat_to_fonction[$cat_id])) {
$data['fonction_label'] = $cat_to_fonction[$cat_id];
}
if (!$data['autre_fonction_label'] && isset($cat_to_autre_fonction[$cat_id])) {
$data['autre_fonction_label'] = $cat_to_autre_fonction[$cat_id];
}
}
return $data;
}