Sélection par séance, réordonnancement et exclusion vie du labo

This commit is contained in:
2026-06-03 00:36:55 +02:00
parent f3970480c4
commit b48f14f0a0
7 changed files with 424 additions and 165 deletions

View File

@@ -21,7 +21,6 @@ class Thalim_NL_Post_Query {
private const SPECIAL_WINDOW_TYPES = [
THALIM_NL_CAT_APPELS => 'datetime_to_fin',
THALIM_NL_CAT_COLLOQUES => 'debut_minus35_to_fin',
THALIM_NL_CAT_SEMINAIRES => 'debut_minus35_to_fin',
THALIM_NL_CAT_COMMS => 'debut_minus35_to_fin',
THALIM_NL_CAT_SOUTENANCES => 'datetime_to_fin',
THALIM_NL_CAT_OUVRAGES => 'datetime_plus3m',
@@ -30,8 +29,17 @@ class Thalim_NL_Post_Query {
private const DEFAULT_WINDOW_TYPE = 'datetime_plus35d';
/**
* Séminaires (cat 11) are not selected as whole posts: instead, each of
* their séances (cat 12) is individually selectable. A séance is eligible
* when its date_de_debut falls within the newsletter month, extended by
* this margin (in days) past the end of the month.
*/
private const SEANCE_WINDOW_MARGIN_DAYS = 5;
/** Categories to exclude from the newsletter UI */
private const EXCLUDED_CATS = [
9, // Vie du labo (intranet)
12, // Séance de séminaire
20, // Newsletter
31, // Non classé
@@ -119,6 +127,15 @@ class Thalim_NL_Post_Query {
$result = [];
foreach (self::get_all_eligible_cat_ids() as $cat_id) {
// Seminars: list individually-selectable séances grouped by seminar.
if ($cat_id === THALIM_NL_CAT_SEMINAIRES) {
$seances = $this->query_seminar_seances($month_start, $month_end);
if (!empty($seances)) {
$result[$cat_id] = $seances;
}
continue;
}
$window_type = self::get_window_type($cat_id);
$posts = $this->query_category($cat_id, $window_type, $month_start, $month_end);
if (!empty($posts)) {
@@ -128,6 +145,100 @@ class Thalim_NL_Post_Query {
return $result;
}
/**
* Build the flat list of selectable séances for the seminar category.
*
* We walk every published seminar (cat 11), read its `seances` meta
* (array of séance post IDs), and keep the séances whose date_de_debut
* falls within [month_start, month_end + SEANCE_WINDOW_MARGIN_DAYS].
* Each returned item carries its parent seminar (id/title/permalink) so
* the UI and the exporter can group séances under their seminar.
*
* @return array Flat list of séance items (see shape below).
*/
private function query_seminar_seances(int $month_start, int $month_end): array {
global $wpdb;
$window_end = $month_end + (self::SEANCE_WINDOW_MARGIN_DAYS * DAY_IN_SECONDS);
$seminar_ids = $wpdb->get_col($wpdb->prepare(
"SELECT DISTINCT p.ID
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'category' AND tt.term_id = %d
WHERE p.post_type = 'post' AND p.post_status = 'publish'",
THALIM_NL_CAT_SEMINAIRES
));
$items = [];
foreach ($seminar_ids as $seminar_id) {
$seminar_id = (int) $seminar_id;
$seminar_title = get_the_title($seminar_id);
$seminar_permalink = get_permalink($seminar_id);
foreach (get_post_meta($seminar_id, 'seances', false) as $sid) {
$sid = (int) $sid;
$s_post = get_post($sid);
if (!$s_post || $s_post->post_status !== 'publish') {
continue;
}
$raw_debut = get_post_meta($sid, 'date_de_debut', true) ?: '';
$ts = $raw_debut ? strtotime($raw_debut) : false;
if (!$ts || $ts < $month_start || $ts > $window_end) {
continue;
}
$items[] = [
'id' => $sid,
'title' => get_the_title($sid),
// Links straight to the séance anchor on the seminar page.
'permalink' => $seminar_permalink . '#seance-' . $sid,
'datetime' => '',
'date_debut' => $raw_debut,
'date_fin' => get_post_meta($sid, 'date_de_fin', true) ?: '',
'post_date' => $s_post->post_date,
'heure_de_debut' => substr(get_post_meta($sid, 'heure_de_debut', true) ?: '', 0, 5),
'heure_de_fin' => substr(get_post_meta($sid, 'heure_de_fin', true) ?: '', 0, 5),
'lieu' => get_post_meta($sid, 'lieu', true) ?: '',
'membres' => $this->get_post_membres($sid),
'autrepersonnes' => get_post_meta($sid, 'autrepersonnes', true) ?: '',
'seminar_id' => $seminar_id,
'seminar_title' => $seminar_title,
'seminar_permalink' => $seminar_permalink,
];
}
}
// Sort by séance date: seminars naturally order by their earliest séance
// when grouped by first appearance (see render / export grouping).
usort($items, fn($a, $b) => strcmp($a['date_debut'], $b['date_debut']));
return $items;
}
/**
* Resolve the parent seminar (cat 11) that lists a given séance in its
* `seances` meta. Mirrors the reverse lookup used by the theme's
* #seance-{ID} redirect. Returns 0 when none is found.
*/
public static function get_seminar_id_for_seance(int $seance_id): int {
global $wpdb;
$parent_id = $wpdb->get_var($wpdb->prepare(
"SELECT pm.post_id
FROM {$wpdb->postmeta} pm
JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = 'seances' AND pm.meta_value = %s
AND p.post_status = 'publish'
LIMIT 1",
(string) $seance_id
));
return (int) $parent_id;
}
/**
* Query a single category with the appropriate window expression.
*/
@@ -190,7 +301,7 @@ class Thalim_NL_Post_Query {
$posts = [];
foreach ($rows as $row) {
$posts[] = $this->build_post_data((int) $row->ID, $row->post_title, $row->post_date, $month_start, $month_end);
$posts[] = $this->build_post_data((int) $row->ID, $row->post_title, $row->post_date);
}
return $posts;
}
@@ -289,8 +400,8 @@ class Thalim_NL_Post_Query {
// Post data builder
// -------------------------------------------------------------------------
private function build_post_data(int $post_id, string $post_title, string $post_date, int $month_start = 0, int $month_end = 0): array {
$data = [
private function build_post_data(int $post_id, string $post_title, string $post_date): array {
return [
'id' => $post_id,
'title' => $post_title,
'permalink' => get_permalink($post_id),
@@ -300,57 +411,7 @@ class Thalim_NL_Post_Query {
'post_date' => $post_date,
'membres' => $this->get_post_membres($post_id),
'autrepersonnes' => get_post_meta($post_id, 'autrepersonnes', true) ?: '',
'seances' => [],
];
// For seminars, fetch séances within the month window
if ($month_start && $month_end && has_category(THALIM_NL_CAT_SEMINAIRES, $post_id)) {
$data['seances'] = $this->get_seances_in_window($post_id, $month_start, $month_end);
}
return $data;
}
/**
* Fetch séances linked to a seminar that fall within the given time window.
*/
private function get_seances_in_window(int $seminar_id, int $month_start, int $month_end): array {
$seance_ids = get_post_meta($seminar_id, 'seances', false);
if (empty($seance_ids)) {
return [];
}
$seances = [];
foreach ($seance_ids as $sid) {
$sid = (int) $sid;
$post = get_post($sid);
if (!$post || $post->post_status !== 'publish') {
continue;
}
$raw_debut = get_post_meta($sid, 'date_de_debut', true) ?: '';
$ts = $raw_debut ? strtotime($raw_debut) : false;
if (!$ts || $ts < $month_start || $ts > $month_end) {
continue;
}
$seances[] = [
'id' => $sid,
'title' => get_the_title($sid),
'date_debut' => $raw_debut,
'date_fin' => get_post_meta($sid, 'date_de_fin', true) ?: '',
'heure_de_debut' => substr(get_post_meta($sid, 'heure_de_debut', true) ?: '', 0, 5),
'heure_de_fin' => substr(get_post_meta($sid, 'heure_de_fin', true) ?: '', 0, 5),
'lieu' => get_post_meta($sid, 'lieu', true) ?: '',
];
}
// Sort by date_de_debut ascending
usort($seances, function ($a, $b) {
return strcmp($a['date_debut'], $b['date_debut']);
});
return $seances;
}
/**