Catégories résolues par slug et IDs Pods par nom, tests
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
17
phpcs.xml.dist
Normal file
17
phpcs.xml.dist
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="THALIM thalim-newsletter">
|
||||
<description>WordPress Coding Standards pour thalim-newsletter.</description>
|
||||
<!-- Installation : composer global require wp-coding-standards/wpcs dealerdirect/phpcodesniffer-composer-installer
|
||||
Exécution depuis ce dossier : phpcs -->
|
||||
<file>.</file>
|
||||
<exclude-pattern>tests/*</exclude-pattern>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg value="sp"/>
|
||||
<rule ref="WordPress-Core">
|
||||
<exclude name="Universal.Arrays.DisallowShortArraySyntax"/>
|
||||
<exclude name="WordPress.WhiteSpace.PrecisionAlignment"/>
|
||||
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent"/>
|
||||
</rule>
|
||||
<rule ref="WordPress.Security"/>
|
||||
<rule ref="WordPress.DB.PreparedSQL"/>
|
||||
</ruleset>
|
||||
67
tests/run-tests.php
Normal file
67
tests/run-tests.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Tests du plugin THALIM Newsletter.
|
||||
*
|
||||
* Exécution (environnement Docker de dev) :
|
||||
* docker exec wordpress php /var/www/html/wp-content/plugins/thalim-newsletter/tests/run-tests.php
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
exit("CLI only\n");
|
||||
}
|
||||
|
||||
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
$_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
|
||||
require dirname(__DIR__, 4) . '/wp-load.php';
|
||||
|
||||
$failures = 0;
|
||||
$count = 0;
|
||||
|
||||
function check(string $name, $actual, $expected): void {
|
||||
global $failures, $count;
|
||||
$count++;
|
||||
if ($actual === $expected) {
|
||||
echo " ok $name\n";
|
||||
} else {
|
||||
$failures++;
|
||||
echo " FAIL $name\n attendu: " . var_export($expected, true)
|
||||
. "\n obtenu : " . var_export($actual, true) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "== constantes de catégories (résolution par slug) ==\n";
|
||||
check('APPELS = 8', THALIM_NL_CAT_APPELS, 8);
|
||||
check('COLLOQUES = 10', THALIM_NL_CAT_COLLOQUES, 10);
|
||||
check('SEMINAIRES = 11', THALIM_NL_CAT_SEMINAIRES, 11);
|
||||
check('COMMS = 13', THALIM_NL_CAT_COMMS, 13);
|
||||
check('SOUTENANCES = 14', THALIM_NL_CAT_SOUTENANCES, 14);
|
||||
check('OUVRAGES = 15', THALIM_NL_CAT_OUVRAGES, 15);
|
||||
check('ARTICLES = 16', THALIM_NL_CAT_ARTICLES, 16);
|
||||
check('NEWSLETTER = 20', THALIM_NL_CAT_NEWSLETTER, 20);
|
||||
|
||||
echo "== fenêtres de dates par catégorie ==\n";
|
||||
check('appels → datetime_to_fin', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_APPELS), 'datetime_to_fin');
|
||||
check('colloques → debut_minus35_to_fin', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_COLLOQUES), 'debut_minus35_to_fin');
|
||||
check('comms → debut_minus35_to_fin', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_COMMS), 'debut_minus35_to_fin');
|
||||
check('soutenances → datetime_to_fin', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_SOUTENANCES), 'datetime_to_fin');
|
||||
check('ouvrages → datetime_plus3m', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_OUVRAGES), 'datetime_plus3m');
|
||||
check('articles → datetime_plus3m', Thalim_NL_Post_Query::get_window_type(THALIM_NL_CAT_ARTICLES), 'datetime_plus3m');
|
||||
check('autre cat → datetime_plus35d', Thalim_NL_Post_Query::get_window_type(99999), 'datetime_plus35d');
|
||||
|
||||
echo "== catégories éligibles ==\n";
|
||||
$eligible = Thalim_NL_Post_Query::get_all_eligible_cat_ids();
|
||||
check('exclut séances (12)', in_array(12, $eligible, true), false);
|
||||
check('exclut vie du labo (9)', in_array(9, $eligible, true), false);
|
||||
check('exclut newsletter (20)', in_array(20, $eligible, true), false);
|
||||
check('exclut non classé (31)', in_array(31, $eligible, true), false);
|
||||
check('inclut séminaires (11)', in_array(11, $eligible, true), true);
|
||||
|
||||
echo "== get_posts_for_month (sanity) ==\n";
|
||||
$q = new Thalim_NL_Post_Query();
|
||||
check('mois invalide → []', $q->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);
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user