Pages Compétences, Contact et Qui suis-je
This commit is contained in:
parent
e06ad42369
commit
da05adede1
10 changed files with 1242 additions and 0 deletions
210
inc/proofs.php
Normal file
210
inc/proofs.php
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
/**
|
||||
* Compétences de la page /competences/ (bloc « Chaque compétence a son projet »).
|
||||
*
|
||||
* Stockage : option `yb_proofs` = liste de [ 'skill' => nom, 'items' => [ID, ID…] ].
|
||||
* Les items sont des réalisations ou des articles ; le premier est la carte
|
||||
* de devant dans le paquet. Nom court d'un article sur les cartes : méta `_yb_short_label`.
|
||||
*
|
||||
* Admin : Profil → Compétences (créer / renommer / réordonner / supprimer
|
||||
* des compétences, y ajouter des réalisations et des articles).
|
||||
*/
|
||||
|
||||
/** Liste prête pour le gabarit : [ nom => [ [post, kind, name, tag, thumb, wipe], … ] ]. */
|
||||
function yb2026_get_proofs()
|
||||
{
|
||||
$rows = get_option('yb_proofs', array());
|
||||
$out = array();
|
||||
foreach ((array) $rows as $row) {
|
||||
$links = array();
|
||||
foreach ((array) ($row['items'] ?? array()) as $id) {
|
||||
$p = get_post((int) $id);
|
||||
if (!$p || $p->post_status !== 'publish' || !in_array($p->post_type, array('realisation', 'post'), true)) continue;
|
||||
$is_post = $p->post_type === 'post';
|
||||
$short = $is_post ? (string) get_post_meta($p->ID, '_yb_short_label', true) : '';
|
||||
$cat = $is_post ? get_the_category($p->ID) : array();
|
||||
$links[] = array(
|
||||
'post' => $p,
|
||||
'kind' => $is_post ? 'article' : 'projet',
|
||||
'name' => $short !== '' ? $short : yb2026_short_title($p),
|
||||
'tag' => $is_post ? 'ARTICLE · ' . mb_strtoupper($cat ? $cat[0]->name : 'BLOG') : (string) get_post_meta($p->ID, '_yb_tag', true),
|
||||
'thumb' => get_the_post_thumbnail_url($p, 'large'),
|
||||
'wipe' => $is_post ? 'LECTURE' : mb_strtoupper(yb2026_short_title($p)),
|
||||
);
|
||||
}
|
||||
if ($links && !empty($row['skill'])) $out[$row['skill']] = $links;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/* ── Admin ──────────────────────────────────────────────────────────── */
|
||||
|
||||
add_action('admin_menu', function () {
|
||||
add_submenu_page('yb-profil', 'Compétences', 'Compétences', 'edit_pages', 'yb-competences', 'yb2026_proofs_page');
|
||||
});
|
||||
|
||||
function yb2026_proofs_page()
|
||||
{
|
||||
$rows = (array) get_option('yb_proofs', array());
|
||||
$reals = get_posts(array('post_type' => 'realisation', 'post_status' => 'publish', 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC'));
|
||||
$arts = get_posts(array('post_type' => 'post', 'post_status' => 'publish', 'numberposts' => -1));
|
||||
$label = function ($p) {
|
||||
$s = $p->post_type === 'post' ? (string) get_post_meta($p->ID, '_yb_short_label', true) : '';
|
||||
return ($p->post_type === 'post' ? 'Article · ' : 'Réalisation · ') . ($s !== '' ? $s : yb2026_short_title($p));
|
||||
};
|
||||
$all = array();
|
||||
foreach (array_merge($reals, $arts) as $p) $all[$p->ID] = $label($p);
|
||||
|
||||
/* Contenus rattachés à aucune compétence : signalés en haut de page */
|
||||
$used = array();
|
||||
foreach ($rows as $r) foreach ((array) ($r['items'] ?? array()) as $id) $used[(int) $id] = true;
|
||||
$orphans = array_diff_key($all, $used);
|
||||
|
||||
$options = function () use ($reals, $arts, $label) {
|
||||
$h = '<option value="">+ Ajouter une réalisation ou un article…</option><optgroup label="Réalisations">';
|
||||
foreach ($reals as $p) $h .= '<option value="' . (int) $p->ID . '">' . esc_html($label($p)) . '</option>';
|
||||
$h .= '</optgroup><optgroup label="Articles du blog">';
|
||||
foreach ($arts as $p) $h .= '<option value="' . (int) $p->ID . '">' . esc_html($label($p)) . '</option>';
|
||||
return $h . '</optgroup>';
|
||||
};
|
||||
$chip = function ($id, $row) use ($all) {
|
||||
return '<span class="ybp-chip" draggable="true"><input type="hidden" name="yb_proofs[' . esc_attr($row) . '][items][]" value="' . (int) $id . '">'
|
||||
. esc_html($all[$id] ?? ('#' . $id)) . ' <button type="button" class="ybp-chip__x" aria-label="Retirer">✕</button></span>';
|
||||
};
|
||||
?>
|
||||
<div class="wrap ybp">
|
||||
<h1>Compétences</h1>
|
||||
<p>Chaque compétence apparaît sur la page <a href="<?php echo esc_url(home_url('/competences/')); ?>" target="_blank">Compétences</a>, dans « Chaque compétence a son projet ». Le <strong>premier</strong> contenu d'une compétence est la carte de devant ; avec plusieurs contenus, un clic ouvre le menu circulaire.</p>
|
||||
<?php if (isset($_GET['updated'])) : ?><div class="notice notice-success is-dismissible"><p>Compétences enregistrées.</p></div><?php endif; ?>
|
||||
<?php if ($orphans) : ?>
|
||||
<div class="notice notice-warning"><p><strong>Pas encore rattachés :</strong> <?php echo esc_html(implode(', ', $orphans)); ?></p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
|
||||
<input type="hidden" name="action" value="yb_save_proofs">
|
||||
<?php wp_nonce_field('yb_save_proofs'); ?>
|
||||
|
||||
<div id="ybp-rows">
|
||||
<?php foreach ($rows as $i => $r) : ?>
|
||||
<div class="ybp-row">
|
||||
<div class="ybp-row__head">
|
||||
<input type="text" class="ybp-skill" name="yb_proofs[<?php echo (int) $i; ?>][skill]" value="<?php echo esc_attr($r['skill'] ?? ''); ?>" placeholder="Nom de la compétence">
|
||||
<button type="button" class="button" data-act="up" aria-label="Monter">↑</button>
|
||||
<button type="button" class="button" data-act="down" aria-label="Descendre">↓</button>
|
||||
<button type="button" class="button button-link-delete" data-act="del">Supprimer</button>
|
||||
</div>
|
||||
<div class="ybp-chips" data-row="<?php echo (int) $i; ?>"><?php foreach ((array) ($r['items'] ?? array()) as $id) echo $chip($id, $i); ?></div>
|
||||
<select class="ybp-add"><?php echo $options(); ?></select>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<template id="ybp-tpl">
|
||||
<div class="ybp-row">
|
||||
<div class="ybp-row__head">
|
||||
<input type="text" class="ybp-skill" name="yb_proofs[__i__][skill]" value="" placeholder="Nom de la compétence">
|
||||
<button type="button" class="button" data-act="up" aria-label="Monter">↑</button>
|
||||
<button type="button" class="button" data-act="down" aria-label="Descendre">↓</button>
|
||||
<button type="button" class="button button-link-delete" data-act="del">Supprimer</button>
|
||||
</div>
|
||||
<div class="ybp-chips" data-row="__i__"></div>
|
||||
<select class="ybp-add"><?php echo $options(); ?></select>
|
||||
</div>
|
||||
</template>
|
||||
<p><button type="button" class="button" id="ybp-new">+ Nouvelle compétence</button></p>
|
||||
|
||||
<h2>Noms courts des articles</h2>
|
||||
<p class="description">Affichés sur les cartes et dans le menu circulaire (les titres d'articles sont souvent trop longs). Laisser vide pour utiliser le début du titre.</p>
|
||||
<table class="widefat striped ybp-labels">
|
||||
<tbody>
|
||||
<?php foreach ($arts as $p) : ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html(get_the_title($p)); ?></td>
|
||||
<td><input type="text" name="yb_short_label[<?php echo (int) $p->ID; ?>]" value="<?php echo esc_attr((string) get_post_meta($p->ID, '_yb_short_label', true)); ?>" placeholder="<?php echo esc_attr(yb2026_short_title($p)); ?>"></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php submit_button('Enregistrer les compétences'); ?>
|
||||
</form>
|
||||
</div>
|
||||
<style>
|
||||
.ybp-row { background: #fff; border: 1px solid #c3c4c7; border-radius: 6px; padding: 14px 16px; margin: 0 0 12px; max-width: 980px; }
|
||||
.ybp-row__head { display: flex; gap: 6px; align-items: center; margin-bottom: 10px; }
|
||||
.ybp-skill { flex: 1; font-size: 15px; font-weight: 600; }
|
||||
.ybp-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 30px; margin-bottom: 8px; }
|
||||
.ybp-chips:empty::before { content: 'Aucun contenu : ajoute une réalisation ou un article ci-dessous.'; color: #8c8f94; font-style: italic; line-height: 30px; }
|
||||
.ybp-chip { display: inline-flex; align-items: center; gap: 6px; background: #f0f6fc; border: 1px solid #72aee6; border-radius: 99px; padding: 3px 6px 3px 12px; cursor: grab; }
|
||||
.ybp-chip:first-child { background: #edfaef; border-color: #00a32a; font-weight: 600; }
|
||||
.ybp-chip:first-child::before { content: 'devant'; font-size: 10px; text-transform: uppercase; color: #00a32a; }
|
||||
.ybp-chip.is-drag { opacity: .4; }
|
||||
.ybp-chip__x { border: 0; background: none; cursor: pointer; color: #b32d2e; padding: 0 4px; }
|
||||
.ybp-add { max-width: 420px; }
|
||||
.ybp-labels { max-width: 980px; }
|
||||
.ybp-labels td:last-child input { width: 100%; }
|
||||
</style>
|
||||
<script>
|
||||
(function () {
|
||||
const rowsEl = document.getElementById('ybp-rows');
|
||||
let n = Date.now();
|
||||
const chipHTML = (id, row, text) => '<span class="ybp-chip" draggable="true"><input type="hidden" name="yb_proofs[' + row + '][items][]" value="' + id + '">'
|
||||
+ text.replace(/[&<>]/g, c => ({ '&': '&', '<': '<', '>': '>' }[c])) + ' <button type="button" class="ybp-chip__x" aria-label="Retirer">✕</button></span>';
|
||||
document.getElementById('ybp-new').addEventListener('click', () => {
|
||||
rowsEl.insertAdjacentHTML('beforeend', document.getElementById('ybp-tpl').innerHTML.replace(/__i__/g, 'n' + (n++)));
|
||||
rowsEl.lastElementChild.querySelector('.ybp-skill').focus();
|
||||
});
|
||||
document.addEventListener('change', e => {
|
||||
const sel = e.target.closest('.ybp-add'); if (!sel || !sel.value) return;
|
||||
const chips = sel.parentNode.querySelector('.ybp-chips');
|
||||
if (!chips.querySelector('input[value="' + sel.value + '"]')) {
|
||||
chips.insertAdjacentHTML('beforeend', chipHTML(sel.value, chips.dataset.row, sel.options[sel.selectedIndex].text));
|
||||
}
|
||||
sel.value = '';
|
||||
});
|
||||
document.addEventListener('click', e => {
|
||||
const x = e.target.closest('.ybp-chip__x'); if (x) { x.closest('.ybp-chip').remove(); return; }
|
||||
const b = e.target.closest('[data-act]'); if (!b) return;
|
||||
const row = b.closest('.ybp-row');
|
||||
if (b.dataset.act === 'del' && confirm('Supprimer cette compétence ?')) row.remove();
|
||||
if (b.dataset.act === 'up' && row.previousElementSibling) row.parentNode.insertBefore(row, row.previousElementSibling);
|
||||
if (b.dataset.act === 'down' && row.nextElementSibling) row.parentNode.insertBefore(row.nextElementSibling, row);
|
||||
});
|
||||
/* Glisser-déposer des pastilles pour changer l'ordre (la première = carte de devant) */
|
||||
let drag = null;
|
||||
document.addEventListener('dragstart', e => { drag = e.target.closest('.ybp-chip'); if (drag) drag.classList.add('is-drag'); });
|
||||
document.addEventListener('dragend', () => { if (drag) drag.classList.remove('is-drag'); drag = null; });
|
||||
document.addEventListener('dragover', e => {
|
||||
if (!drag) return;
|
||||
const over = e.target.closest('.ybp-chip'), box = e.target.closest('.ybp-chips');
|
||||
if (!box || box !== drag.parentNode) return;
|
||||
e.preventDefault();
|
||||
if (over && over !== drag) {
|
||||
const r = over.getBoundingClientRect();
|
||||
box.insertBefore(drag, (e.clientX - r.left) > r.width / 2 ? over.nextSibling : over);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
add_action('admin_post_yb_save_proofs', function () {
|
||||
if (!current_user_can('edit_pages')) wp_die('Accès refusé.');
|
||||
check_admin_referer('yb_save_proofs');
|
||||
$clean = array();
|
||||
foreach ((array) wp_unslash($_POST['yb_proofs'] ?? array()) as $r) {
|
||||
$skill = sanitize_text_field($r['skill'] ?? '');
|
||||
$items = array_values(array_unique(array_filter(array_map('intval', (array) ($r['items'] ?? array())))));
|
||||
if ($skill === '' && !$items) continue;
|
||||
$clean[] = array('skill' => $skill !== '' ? $skill : 'Sans nom', 'items' => $items);
|
||||
}
|
||||
update_option('yb_proofs', $clean, false);
|
||||
foreach ((array) wp_unslash($_POST['yb_short_label'] ?? array()) as $id => $lbl) {
|
||||
$id = (int) $id;
|
||||
if (get_post_type($id) !== 'post') continue;
|
||||
$lbl = sanitize_text_field($lbl);
|
||||
$lbl === '' ? delete_post_meta($id, '_yb_short_label') : update_post_meta($id, '_yb_short_label', $lbl);
|
||||
}
|
||||
wp_safe_redirect(admin_url('admin.php?page=yb-competences&updated=1'));
|
||||
exit;
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue