diff --git a/includes/class-admin-page.php b/includes/class-admin-page.php index 1cac891..64fa7de 100644 --- a/includes/class-admin-page.php +++ b/includes/class-admin-page.php @@ -62,7 +62,6 @@ class Thalim_NL_Admin_Page { 'meta_key' => '_newsletter_month', 'orderby' => 'meta_value', 'order' => 'DESC', - 'lang' => '', ]); $past_newsletters = array_filter($past_newsletters, function ($p) { return !empty(get_post_meta($p->ID, '_newsletter_month', true)); @@ -490,15 +489,40 @@ class Thalim_NL_Admin_Page { $this->do_triple_storage_category($post_id, THALIM_NL_CAT_NEWSLETTER); - if (function_exists('pll_set_post_language')) { - pll_set_post_language($post_id, 'fr'); - } - return $post_id; } /** - * Assign a category using the Pods triple-storage pattern (same as HAL importer). + * Résout l'ID du pod `post` et celui de son champ `categorie` par NOM + * dans wp_posts (post_type _pods_pod / _pods_field) — les IDs en dur ne + * survivent pas à une réimportation de base. + * + * @return array{0:int,1:int} [pod_id, field_id] (0 si introuvable) + */ + private function resolve_pods_categorie_ids(): array { + static $ids = null; + if ($ids === null) { + global $wpdb; + $pod_id = (int) $wpdb->get_var( + "SELECT ID FROM {$wpdb->posts} + WHERE post_type = '_pods_pod' AND post_name = 'post' LIMIT 1" + ); + $field_id = $pod_id ? (int) $wpdb->get_var($wpdb->prepare( + "SELECT ID FROM {$wpdb->posts} + WHERE post_type = '_pods_field' AND post_name = 'categorie' AND post_parent = %d + LIMIT 1", + $pod_id + )) : 0; + $ids = [$pod_id, $field_id]; + } + return $ids; + } + + /** + * Assign a category using the Pods quadruple-storage pattern (same as HAL importer). + * + * ⚠ DÉPENDANCE DURE à Pods 3.x : reproduit le stockage interne de Pods + * (postmeta + _pods_* sérialisé + wp_podsrel + catégorie WP native). */ private function do_triple_storage_category(int $post_id, int $cat_id): void { global $wpdb; @@ -513,11 +537,19 @@ class Thalim_NL_Admin_Page { update_post_meta($post_id, '_pods_categorie', [$cat_id]); // 4. wp_podsrel row + [$pod_id, $field_id] = $this->resolve_pods_categorie_ids(); + if (!$pod_id || !$field_id) { + error_log(sprintf( + '[thalim-newsletter] Pod/champ Pods "categorie" introuvable — wp_podsrel non écrit pour le post %d', + $post_id + )); + return; + } $wpdb->delete( $wpdb->prefix . 'podsrel', [ - 'pod_id' => THALIM_NL_POD_ID_POST, - 'field_id' => THALIM_NL_FIELD_ID_CAT, + 'pod_id' => $pod_id, + 'field_id' => $field_id, 'item_id' => $post_id, ], ['%d', '%d', '%d'] @@ -525,8 +557,8 @@ class Thalim_NL_Admin_Page { $wpdb->insert( $wpdb->prefix . 'podsrel', [ - 'pod_id' => THALIM_NL_POD_ID_POST, - 'field_id' => THALIM_NL_FIELD_ID_CAT, + 'pod_id' => $pod_id, + 'field_id' => $field_id, 'item_id' => $post_id, 'related_pod_id' => 0, 'related_field_id' => 0, @@ -617,7 +649,6 @@ class Thalim_NL_Admin_Page { 'posts_per_page' => 1, 'meta_key' => '_newsletter_month', 'meta_value' => $year_month, - 'lang' => '', // Polylang: all languages ]); return $posts[0] ?? null; } diff --git a/includes/class-post-query.php b/includes/class-post-query.php index 0549884..1add71c 100644 --- a/includes/class-post-query.php +++ b/includes/class-post-query.php @@ -37,13 +37,27 @@ class Thalim_NL_Post_Query { */ 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é - ]; + /** + * Categories to exclude from the newsletter UI, résolues par slug + * (fallback sur les IDs historiques si un slug est introuvable). + */ + private static function excluded_cats(): array { + static $ids = null; + if ($ids === null) { + $map = [ + 'vie-du-labo-intranet' => 9, // Vie du labo (intranet) + 'seance-de-seminaire' => 12, // Séance de séminaire + 'newsletter' => 20, // Newsletter + 'non-classe' => 31, // Non classé + ]; + $ids = []; + foreach ($map as $slug => $fallback) { + $term = get_term_by('slug', $slug, 'category'); + $ids[] = $term ? (int) $term->term_id : $fallback; + } + } + return $ids; + } /** * Get all newsletter-eligible categories, grouped by parent. @@ -61,7 +75,7 @@ class Thalim_NL_Post_Query { $parents = []; foreach ($all_cats as $cat) { - if (in_array($cat->term_id, self::EXCLUDED_CATS, true)) { + if (in_array($cat->term_id, self::excluded_cats(), true)) { continue; } if ($cat->parent == 0) { @@ -74,7 +88,7 @@ class Thalim_NL_Post_Query { } foreach ($all_cats as $cat) { - if (in_array($cat->term_id, self::EXCLUDED_CATS, true)) { + if (in_array($cat->term_id, self::excluded_cats(), true)) { continue; } if ($cat->parent == 0) { diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..1dfe150 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,17 @@ + + + WordPress Coding Standards pour thalim-newsletter. + + . + tests/* + + + + + + + + + + diff --git a/tests/run-tests.php b/tests/run-tests.php new file mode 100644 index 0000000..6578e84 --- /dev/null +++ b/tests/run-tests.php @@ -0,0 +1,67 @@ +get_posts_for_month('garbage'), []); +$month = $q->get_posts_for_month(date('Y-m')); +check('mois courant → array', is_array($month), true); + +echo "\n$count tests, $failures échec(s)\n"; +exit($failures ? 1 : 0); diff --git a/thalim-newsletter.php b/thalim-newsletter.php index 15db785..5b998d1 100644 --- a/thalim-newsletter.php +++ b/thalim-newsletter.php @@ -19,19 +19,42 @@ define('THALIM_NL_VERSION', '1.0.0'); define('THALIM_NL_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('THALIM_NL_PLUGIN_URL', plugin_dir_url(__FILE__)); -// Category IDs (verified in DB 2026-03-20) -define('THALIM_NL_CAT_APPELS', 8); -define('THALIM_NL_CAT_COLLOQUES', 10); -define('THALIM_NL_CAT_SEMINAIRES', 11); -define('THALIM_NL_CAT_COMMS', 13); -define('THALIM_NL_CAT_SOUTENANCES', 14); -define('THALIM_NL_CAT_OUVRAGES', 15); -define('THALIM_NL_CAT_ARTICLES', 16); -define('THALIM_NL_CAT_NEWSLETTER', 20); - -// Pods IDs (verified in DB 2026-03-20) -define('THALIM_NL_POD_ID_POST', 8); -define('THALIM_NL_FIELD_ID_CAT', 16); +// Catégories : résolues par slug au chargement (les term_ids auto-incrémentés +// ne survivent pas à une réimportation de base). Fallback sur les IDs +// historiques (vérifiés en base 2026-03-20) si un slug est introuvable. +// Une seule requête SQL, mise en cache 1 jour (transient). +(function () { + $cats = [ + 'THALIM_NL_CAT_APPELS' => ['appels-a-contribution', 8], + 'THALIM_NL_CAT_COLLOQUES' => ['colloques-et-journees-detudes', 10], + 'THALIM_NL_CAT_SEMINAIRES' => ['seminaires', 11], + 'THALIM_NL_CAT_COMMS' => ['communications', 13], + 'THALIM_NL_CAT_SOUTENANCES' => ['soutenances', 14], + 'THALIM_NL_CAT_OUVRAGES' => ['ouvrages', 15], + 'THALIM_NL_CAT_ARTICLES' => ['articles', 16], + 'THALIM_NL_CAT_NEWSLETTER' => ['newsletter', 20], + ]; + $by_slug = get_transient('thalim_nl_cat_ids'); + if (!is_array($by_slug)) { + global $wpdb; + $slugs = array_column($cats, 0); + $placeholders = implode(',', array_fill(0, count($slugs), '%s')); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT t.slug, t.term_id FROM {$wpdb->terms} t + JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = t.term_id + WHERE tt.taxonomy = 'category' AND t.slug IN ($placeholders)", + $slugs + )); + $by_slug = []; + foreach ((array) $rows as $row) { + $by_slug[$row->slug] = (int) $row->term_id; + } + set_transient('thalim_nl_cat_ids', $by_slug, DAY_IN_SECONDS); + } + foreach ($cats as $const => [$slug, $fallback]) { + define($const, $by_slug[$slug] ?? $fallback); + } +})(); /** * Main plugin class — singleton