/* ================================================================== * CHAMPS STRUCTURÉS NATIFS — Services & Projets (sans plugin) * Stockage : post_meta tableaux sérialisés (_nv_service_data / * _nv_projet_data). Rendu : templates/single-service.php & * single-projet.php (rendu serveur centralisé). * Appended: blocs metaboxes. * ================================================================== */ /* ---------- Accesseurs ---------- */ function nv_get_service_data($post_id) { $d = get_post_meta($post_id, '_nv_service_data', true); return is_array($d) ? $d : []; } function nv_get_projet_data($post_id) { $d = get_post_meta($post_id, '_nv_projet_data', true); return is_array($d) ? $d : []; } /* ---------- Assets admin ---------- */ add_action('admin_enqueue_scripts', function ($hook) { if (!in_array($hook, ['post.php', 'post-new.php'], true)) { return; } if (!in_array(get_post_type(), ['service', 'projet'], true)) { return; } wp_enqueue_media(); wp_enqueue_script('nv-admin-fields', get_theme_file_uri('assets/admin-fields.js'), [], NEOVENTIS_VERSION, true); }); /* ---------- Boîtes méta ---------- */ add_action('add_meta_boxes', function () { add_meta_box('nv-service-sections', 'Sections de la page service', 'nv_metabox_service_cb', 'service', 'normal', 'high'); add_meta_box('nv-projet-fiche', 'Fiche technique du projet', 'nv_metabox_projet_cb', 'projet', 'normal', 'high'); }); add_action('admin_head', function () { if (!in_array(get_post_type(), ['service', 'projet'], true)) { return; } echo ''; }); /* Rangée simple : un champ texte */ function _nv_row_simple($name_tpl, $value = '', $placeholder = '') { printf( '
', esc_attr($name_tpl), esc_attr($value), esc_attr($placeholder) ); } /* Rangée double : titre + texte ($idx = __I__ pour le gabarit JS) */ function _nv_row_double($base, $t = '', $d = '', $idx = '__I__') { printf( '
', esc_attr($base), esc_attr($t), esc_attr($d), '', esc_attr($idx) ); } /* ---------- Metabox Service ---------- */ function nv_metabox_service_cb($post) { wp_nonce_field('nv_save_meta', 'nv_meta_nonce'); $d = nv_get_service_data($post->ID); $cas = isset($d['cas_usage']) ? (array) $d['cas_usage'] : []; $av = isset($d['avantages']) ? (array) $d['avantages'] : []; $dem = isset($d['demarche']) ? (array) $d['demarche'] : []; $comp = isset($d['comp']) ? array_map('intval', (array) $d['comp']) : []; $others = get_posts([ 'post_type' => 'service', 'numberposts' => -1, 'orderby' => 'menu_order', 'order' => 'ASC', 'post_status' => 'publish', 'exclude' => $post->ID, ]); echo '

Le corps principal (« Notre approche ») reste l’éditeur Gutenberg ci-dessus. Le sous-titre du bandeau est le champ Extrait. Les sections ci-dessous sont générées automatiquement par le thème.

'; /* Cas d'usage */ echo '

Cas d’usage (liste)

'; foreach ($cas as $v) { _nv_row_simple('nv_sd[cas_usage][]', $v); } echo ''; echo '

'; /* Avantages */ echo '

Avantages de la prestation (cartes numérotées)

'; foreach ($av as $v) { _nv_row_simple('nv_sd[avantages][]', $v); } echo ''; echo '

Laisser vide masque toute la section.

'; /* Démarche */ echo '

Notre démarche (étapes numérotées)

'; foreach ($dem as $i => $r) { _nv_row_double('nv_sd[demarche]', isset($r['t']) ? $r['t'] : '', isset($r['d']) ? $r['d'] : '', (string) $i); } echo ''; echo '

Laisser vide masque toute la section.

'; /* Services complémentaires */ echo '

Services complémentaires (liens)

'; if ($others) { echo '
'; foreach ($others as $o) { printf( '', (int) $o->ID, in_array((int) $o->ID, $comp, true) ? ' checked' : '', esc_html($o->post_title) ); } echo '
'; } else { echo '

Aucun autre service publié.

'; } echo '
'; } /* ---------- Metabox Projet ---------- */ function nv_metabox_projet_cb($post) { wp_nonce_field('nv_save_meta', 'nv_meta_nonce'); $d = nv_get_projet_data($post->ID); $champs = isset($d['champs']) ? (array) $d['champs'] : []; $badges = isset($d['badges']) ? (array) $d['badges'] : []; $logo = isset($d['logo_id']) ? absint($d['logo_id']) : 0; echo '

La fiche technique alimente l’encadré latéral de la page projet. Le résumé du bandeau est le champ Extrait.

'; /* Champs simples */ echo '

Champs (libellé → valeur)

'; foreach ($champs as $i => $r) { printf( '
', (int) $i, esc_attr(isset($r['l']) ? $r['l'] : ''), esc_attr(isset($r['v']) ? $r['v'] : '') ); } echo ''; echo '

'; /* Listes de badges */ echo '

Listes de badges (séparer par des virgules)

'; foreach ($badges as $i => $r) { printf( '
', (int) $i, esc_attr(isset($r['l']) ? $r['l'] : ''), esc_attr(implode(', ', isset($r['tags']) ? (array) $r['tags'] : [])) ); } echo ''; echo '

'; /* Logo client */ $src = $logo ? wp_get_attachment_image_url($logo, 'thumbnail') : ''; echo '

Logo client (optionnel)

'; printf('', $logo); printf('', esc_url($src), $src ? '' : 'display:none'); echo ' '; echo '
'; } /* ---------- Sauvegarde Service ---------- */ add_action('save_post_service', function ($post_id) { if (!isset($_POST['nv_meta_nonce']) || !wp_verify_nonce(sanitize_key($_POST['nv_meta_nonce']), 'nv_save_meta')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } if (!current_user_can('edit_post', $post_id)) { return; } $in = isset($_POST['nv_sd']) ? (array) wp_unslash($_POST['nv_sd']) : []; $dem = []; foreach ((array) (isset($in['demarche']) ? $in['demarche'] : []) as $row) { $row = (array) $row; $t = sanitize_text_field(isset($row['t']) ? $row['t'] : ''); $x = sanitize_text_field(isset($row['d']) ? $row['d'] : ''); if ('' !== $t || '' !== $x) { $dem[] = ['t' => $t, 'd' => $x]; } } $data = [ 'cas_usage' => array_values(array_filter(array_map('sanitize_text_field', (array) (isset($in['cas_usage']) ? $in['cas_usage'] : [])), 'strlen')), 'avantages' => array_values(array_filter(array_map('sanitize_text_field', (array) (isset($in['avantages']) ? $in['avantages'] : [])), 'strlen')), 'demarche' => $dem, 'comp' => array_values(array_filter(array_map('absint', (array) (isset($in['comp']) ? $in['comp'] : [])))), ]; update_post_meta($post_id, '_nv_service_data', $data); }, 10); /* ---------- Sauvegarde Projet ---------- */ add_action('save_post_projet', function ($post_id) { if (!isset($_POST['nv_meta_nonce']) || !wp_verify_nonce(sanitize_key($_POST['nv_meta_nonce']), 'nv_save_meta')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } if (!current_user_can('edit_post', $post_id)) { return; } $in = isset($_POST['nv_pd']) ? (array) wp_unslash($_POST['nv_pd']) : []; $champs = []; foreach ((array) (isset($in['champs']) ? $in['champs'] : []) as $row) { $row = (array) $row; $l = sanitize_text_field(isset($row['l']) ? $row['l'] : ''); $v = sanitize_text_field(isset($row['v']) ? $row['v'] : ''); if ('' !== $l && '' !== $v) { $champs[] = ['l' => $l, 'v' => $v]; } } $badges = []; foreach ((array) (isset($in['badges']) ? $in['badges'] : []) as $row) { $row = (array) $row; $l = sanitize_text_field(isset($row['l']) ? $row['l'] : ''); $tags = array_map('sanitize_text_field', array_values(array_filter(array_map('trim', explode(',', (string) (isset($row['tags']) ? $row['tags'] : ''))), 'strlen'))); if ('' !== $l && $tags) { $badges[] = ['l' => $l, 'tags' => $tags]; } } $data = [ 'champs' => $champs, 'badges' => $badges, 'logo_id' => absint(isset($in['logo_id']) ? $in['logo_id'] : 0), ]; update_post_meta($post_id, '_nv_projet_data', $data); }, 10); https://dev.neoventis.fr/post-sitemap.xml 2026-06-03T08:06:29+00:00 https://dev.neoventis.fr/page-sitemap.xml 2026-08-24T07:27:21+00:00 https://dev.neoventis.fr/projet-sitemap.xml 2026-08-22T17:37:24+00:00 https://dev.neoventis.fr/service-sitemap.xml 2026-08-24T11:43:16+00:00 https://dev.neoventis.fr/category-sitemap.xml 2026-06-03T08:06:29+00:00 https://dev.neoventis.fr/service_univers-sitemap.xml 2026-08-24T11:43:16+00:00