Mode clair / sombre et thèmes de saison
This commit is contained in:
parent
da05adede1
commit
17cf2c8380
19 changed files with 3563 additions and 0 deletions
291
functions.php
Normal file
291
functions.php
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
/**
|
||||
* yb2026 : thème portfolio « sombre & premium »
|
||||
* v3 : porté depuis handoff/reference/portfolio-site-v3.html (+ theme-assets/)
|
||||
*/
|
||||
|
||||
/* ── Assets ─────────────────────────────────────────────────────────── */
|
||||
function yb2026_assets()
|
||||
{
|
||||
$v = '3.0';
|
||||
$uri = get_template_directory_uri();
|
||||
/* Polices hébergées sur le serveur (plus d'appel à Google Fonts : pas de transfert d'IP des visiteurs) */
|
||||
wp_enqueue_style('yb-fonts', $uri . '/assets/fonts/fonts.css', array(), '1');
|
||||
wp_enqueue_style('yb-effects', $uri . '/assets/css/effects.css', array(), $v);
|
||||
wp_enqueue_style('yb-style', $uri . '/assets/css/style.css', array('yb-effects'), $v);
|
||||
/* Ordre : cursor + orb définissent YBCursor/YBOrb, effects.js les initialise */
|
||||
wp_enqueue_script('yb-cursor', $uri . '/assets/js/cursor.js', array(), $v, true);
|
||||
if (is_front_page()) {
|
||||
wp_enqueue_script('yb-orb', $uri . '/assets/js/orb.js', array(), $v, true);
|
||||
}
|
||||
wp_enqueue_script('yb-effects', $uri . '/assets/js/effects.js', array('yb-cursor'), $v, true);
|
||||
if (is_page('qui-suis-je')) {
|
||||
wp_enqueue_script('yb-about', $uri . '/assets/js/about.js', array('yb-effects'), $v . '.1', true);
|
||||
}
|
||||
if (is_page('competences')) {
|
||||
wp_enqueue_script('yb-competences', $uri . '/assets/js/competences.js', array('yb-effects'), $v . '.1', true);
|
||||
}
|
||||
}
|
||||
add_action('wp_enqueue_scripts', 'yb2026_assets');
|
||||
|
||||
require_once get_template_directory() . '/inc/profile.php';
|
||||
require_once get_template_directory() . '/inc/proofs.php';
|
||||
require_once get_template_directory() . '/inc/seasons.php';
|
||||
require_once get_template_directory() . '/inc/errors.php';
|
||||
|
||||
add_theme_support('title-tag');
|
||||
add_theme_support('post-thumbnails');
|
||||
add_theme_support('html5', array('style', 'script'));
|
||||
|
||||
/* ── Custom Post Types (repris du thème précédent, mêmes rewrites) ──── */
|
||||
function yb2026_post_types()
|
||||
{
|
||||
register_post_type('realisation', array(
|
||||
'labels' => array(
|
||||
'name' => 'Réalisations',
|
||||
'singular_name' => 'Réalisation',
|
||||
'add_new_item' => 'Ajouter une réalisation',
|
||||
'edit_item' => 'Modifier la réalisation',
|
||||
),
|
||||
'public' => true,
|
||||
'has_archive' => false,
|
||||
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
|
||||
'menu_icon' => 'dashicons-portfolio',
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => array('slug' => 'realisations'),
|
||||
));
|
||||
|
||||
register_post_type('service', array(
|
||||
'labels' => array(
|
||||
'name' => 'Services',
|
||||
'singular_name' => 'Service',
|
||||
'add_new_item' => 'Ajouter un service',
|
||||
'edit_item' => 'Modifier le service',
|
||||
),
|
||||
'public' => true,
|
||||
'has_archive' => false,
|
||||
'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
|
||||
'menu_icon' => 'dashicons-portfolio',
|
||||
'show_in_rest' => true,
|
||||
'rewrite' => array('slug' => 'services'),
|
||||
));
|
||||
}
|
||||
add_action('init', 'yb2026_post_types');
|
||||
|
||||
function yb2026_flush()
|
||||
{
|
||||
yb2026_post_types();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
add_action('after_switch_theme', 'yb2026_flush');
|
||||
|
||||
/* ── Meta-box réalisation : champs de l'étude de cas ────────────────── */
|
||||
function yb2026_add_metaboxes()
|
||||
{
|
||||
add_meta_box('yb_real_meta', 'Étude de cas (design v2)', 'yb2026_render_real_metabox', 'realisation', 'normal', 'high');
|
||||
}
|
||||
add_action('add_meta_boxes', 'yb2026_add_metaboxes');
|
||||
|
||||
function yb2026_render_real_metabox($post)
|
||||
{
|
||||
wp_nonce_field('yb_real_nonce_action', 'yb_real_nonce');
|
||||
$f = function ($k) use ($post) { return esc_attr(get_post_meta($post->ID, $k, true)); };
|
||||
$sections = get_post_meta($post->ID, '_yb_sections', true);
|
||||
$sections_txt = '';
|
||||
if ($sections) {
|
||||
$arr = json_decode($sections, true);
|
||||
if (is_array($arr)) {
|
||||
foreach ($arr as $s) {
|
||||
$sections_txt .= '/ ' . $s['h'] . "\n" . $s['p'] . "\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<style>.yb-mb label{font-weight:600;display:block;margin:12px 0 4px}.yb-mb input,.yb-mb textarea{width:100%}</style>
|
||||
<div class="yb-mb">
|
||||
<label for="yb_order">Ordre (01–09, utilisé pour la numérotation et le tri)</label>
|
||||
<input type="number" id="yb_order" name="yb_order" value="<?php echo $f('_yb_order'); ?>" min="1" max="99">
|
||||
<label for="yb_tag">Stack / tag (affiché en mono lime, ex. « DEBIAN 12 · DOCKER · WIREGUARD »)</label>
|
||||
<input type="text" id="yb_tag" name="yb_tag" value="<?php echo $f('_yb_tag'); ?>">
|
||||
<label for="yb_year">Année (ex. « 2024–2026 »)</label>
|
||||
<input type="text" id="yb_year" name="yb_year" value="<?php echo $f('_yb_year'); ?>">
|
||||
<label for="yb_role">Rôle (ex. « Admin Sys & Réseaux »)</label>
|
||||
<input type="text" id="yb_role" name="yb_role" value="<?php echo $f('_yb_role'); ?>">
|
||||
<label for="yb_short">Description courte (cartes)</label>
|
||||
<input type="text" id="yb_short" name="yb_short" value="<?php echo $f('_yb_short'); ?>">
|
||||
<label for="yb_desc">Description longue (intro de l'étude de cas)</label>
|
||||
<textarea id="yb_desc" name="yb_desc" rows="3"><?php echo esc_textarea(get_post_meta($post->ID, '_yb_desc', true)); ?></textarea>
|
||||
<label for="yb_note">Note terminal (bordure lime, ex. « $ uptime · en production depuis 2 ans »)</label>
|
||||
<input type="text" id="yb_note" name="yb_note" value="<?php echo $f('_yb_note'); ?>">
|
||||
<label for="yb_sections">Sections de l'étude de cas : format : ligne « / Titre » puis paragraphe, sections séparées par une ligne vide. Laisser vide pour afficher le contenu de l'éditeur à la place.</label>
|
||||
<textarea id="yb_sections" name="yb_sections" rows="12" placeholder="/ Le point de départ Texte du paragraphe… / L'architecture Texte…"><?php echo esc_textarea($sections_txt); ?></textarea>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
function yb2026_save_metaboxes($post_id)
|
||||
{
|
||||
if (
|
||||
!isset($_POST['yb_real_nonce']) ||
|
||||
!wp_verify_nonce($_POST['yb_real_nonce'], 'yb_real_nonce_action') ||
|
||||
!current_user_can('edit_post', $post_id)
|
||||
) return;
|
||||
|
||||
foreach (array('yb_order' => '_yb_order', 'yb_tag' => '_yb_tag', 'yb_year' => '_yb_year', 'yb_role' => '_yb_role', 'yb_short' => '_yb_short', 'yb_note' => '_yb_note') as $field => $key) {
|
||||
if (isset($_POST[$field])) {
|
||||
update_post_meta($post_id, $key, sanitize_text_field($_POST[$field]));
|
||||
}
|
||||
}
|
||||
if (isset($_POST['yb_desc'])) {
|
||||
update_post_meta($post_id, '_yb_desc', sanitize_textarea_field($_POST['yb_desc']));
|
||||
}
|
||||
if (isset($_POST['yb_sections'])) {
|
||||
$raw = str_replace("\r\n", "\n", (string) $_POST['yb_sections']);
|
||||
$out = array();
|
||||
foreach (preg_split('/\n\s*\n/', trim($raw)) as $block) {
|
||||
$lines = explode("\n", trim($block));
|
||||
if (count($lines) < 2 || strpos($lines[0], '/') !== 0) continue;
|
||||
$out[] = array(
|
||||
'h' => sanitize_text_field(ltrim(substr($lines[0], 1))),
|
||||
'p' => sanitize_textarea_field(implode(' ', array_slice($lines, 1))),
|
||||
);
|
||||
}
|
||||
update_post_meta($post_id, '_yb_sections', $out ? wp_json_encode($out, JSON_UNESCAPED_UNICODE) : '');
|
||||
}
|
||||
}
|
||||
add_action('save_post_realisation', 'yb2026_save_metaboxes');
|
||||
|
||||
/* ── Helpers ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/** Toutes les réalisations triées par _yb_order (fallback date). */
|
||||
function yb2026_get_projects($limit = -1)
|
||||
{
|
||||
$q = new WP_Query(array(
|
||||
'post_type' => 'realisation',
|
||||
'posts_per_page' => $limit,
|
||||
'meta_key' => '_yb_order',
|
||||
'orderby' => array('meta_value_num' => 'ASC', 'date' => 'DESC'),
|
||||
'no_found_rows' => true,
|
||||
));
|
||||
return $q->posts;
|
||||
}
|
||||
|
||||
/** Numéro « 01 » d'une réalisation. */
|
||||
function yb2026_num($post_id)
|
||||
{
|
||||
$o = (int) get_post_meta($post_id, '_yb_order', true);
|
||||
return str_pad($o ? $o : 1, 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/** Nom court d'un projet : partie avant le « : » du titre WP (ancien séparateur « : » toléré). */
|
||||
function yb2026_short_title($p)
|
||||
{
|
||||
$t = get_the_title($p);
|
||||
$parts = preg_split('/\s+[:\x{2014}]\s+/u', $t, 2);
|
||||
return $parts[0];
|
||||
}
|
||||
|
||||
/** Carte projet (piste des travaux sur l'accueil + grille réalisations).
|
||||
Classes .card / .card-img attendues par effects.css et effects.js (compteur, hover N&B). */
|
||||
function yb2026_project_card($p, $home = false)
|
||||
{
|
||||
$tag = get_post_meta($p->ID, '_yb_tag', true);
|
||||
if (!$tag) $tag = strtoupper((string) get_post_meta($p->ID, '_real_technologies', true));
|
||||
$short = get_post_meta($p->ID, '_yb_short', true);
|
||||
if (!$short) $short = get_the_excerpt($p);
|
||||
$year = get_post_meta($p->ID, '_yb_year', true);
|
||||
$name = yb2026_short_title($p);
|
||||
?>
|
||||
<a class="card<?php echo $home ? ' card--home' : ''; ?>" href="<?php echo esc_url(get_permalink($p)); ?>" data-cursor-label="VOIR LE PROJET" data-wipe="<?php echo esc_attr(mb_strtoupper($name)); ?>"<?php echo $home ? '' : ' data-reveal'; ?>>
|
||||
<span class="card-img">
|
||||
<?php echo get_the_post_thumbnail($p, 'large', array('loading' => 'lazy', 'alt' => esc_attr($name))); ?>
|
||||
<span class="card-num"><?php echo esc_html(yb2026_num($p->ID)); ?></span>
|
||||
<?php if ($home && $year) : ?><span class="card-year"><?php echo esc_html($year); ?></span><?php endif; ?>
|
||||
</span>
|
||||
<span class="card-body">
|
||||
<span class="card-head"><span class="card-name"><?php echo esc_html($name); ?></span><span class="card-arrow">→</span></span>
|
||||
<span class="card-tag"><?php echo esc_html($tag); ?></span>
|
||||
<span class="card-short"><?php echo esc_html($short); ?></span>
|
||||
</span>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
|
||||
/* ── Noindex partout sauf l'accueil (demande du 06/10/2026) ──────────
|
||||
Seule la page d'accueil reste indexable ; pages, réalisations, articles,
|
||||
archives, recherche, 404… sont en noindex. */
|
||||
add_filter('wp_robots', function ($robots) {
|
||||
if (is_front_page()) {
|
||||
return $robots;
|
||||
}
|
||||
unset($robots['max-image-preview']);
|
||||
$robots['noindex'] = true;
|
||||
$robots['nofollow'] = true;
|
||||
return $robots;
|
||||
}, 99);
|
||||
|
||||
/* Même consigne pour les réponses non-HTML générées par WordPress (flux RSS, REST…) */
|
||||
add_action('send_headers', function () {
|
||||
if (!is_admin() && !is_front_page()) {
|
||||
header('X-Robots-Tag: noindex, nofollow', true);
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Anti-doublon : retire du contenu l'image déjà en mise-en-avant ──── */
|
||||
add_filter('the_content', function ($content) {
|
||||
if (!is_singular('realisation') || !has_post_thumbnail()) {
|
||||
return $content;
|
||||
}
|
||||
$thumb_url = wp_get_attachment_image_url(get_post_thumbnail_id(), 'full');
|
||||
if (!$thumb_url) return $content;
|
||||
|
||||
$basename = pathinfo(parse_url($thumb_url, PHP_URL_PATH), PATHINFO_FILENAME);
|
||||
$basename = preg_quote($basename, '#');
|
||||
|
||||
$content = preg_replace('#<figure\b[^>]*>(?:(?!</figure>).)*?' . $basename . '(?:(?!</figure>).)*?</figure>#is', '', $content);
|
||||
$content = preg_replace('#<img\b[^>]*?' . $basename . '[^>]*?/?>#i', '', $content);
|
||||
$content = preg_replace('#<p>\s*</p>#i', '', $content);
|
||||
return $content;
|
||||
}, 20);
|
||||
|
||||
/* ── Options pages ACF (shim) ────────────────────────────────────────── */
|
||||
add_action('acf/init', function () {
|
||||
if (function_exists('acf_add_options_page')) {
|
||||
acf_add_options_page(array(
|
||||
'page_title' => 'Theme General Settings',
|
||||
'menu_title' => 'Theme Settings',
|
||||
'menu_slug' => 'theme-general-settings',
|
||||
'capability' => 'edit_posts',
|
||||
'redirect' => false,
|
||||
));
|
||||
acf_add_options_sub_page(array('page_title' => 'Theme Header Settings', 'menu_title' => 'Header', 'parent_slug' => 'theme-general-settings'));
|
||||
acf_add_options_sub_page(array('page_title' => 'Theme Social Settings', 'menu_title' => 'Social', 'parent_slug' => 'theme-general-settings'));
|
||||
acf_add_options_sub_page(array('page_title' => 'Theme Coordonnée Settings', 'menu_title' => 'Coordonnée', 'parent_slug' => 'theme-general-settings'));
|
||||
}
|
||||
});
|
||||
|
||||
/* Autoptimize : ne pas agréger le script inline anti-flash du wipe
|
||||
(il doit s'exécuter immédiatement après l'élément #yb-wipe, avant le paint). */
|
||||
add_filter('autoptimize_filter_js_exclude', function ($exclude) {
|
||||
return $exclude . ', ybWipe, ybTheme';
|
||||
});
|
||||
|
||||
/* Shortcode hérité (présent dans d'anciens contenus) */
|
||||
add_shortcode('tavu', function () {
|
||||
return "<h1>T'a vue ?</h1>";
|
||||
});
|
||||
|
||||
/* Ancienne adresse de la page Compétences : /services/ → /competences/ (301).
|
||||
Les contenus du CPT « service » gardent leurs adresses /services/<slug>/. */
|
||||
add_action('template_redirect', function () {
|
||||
$path = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH), '/');
|
||||
if ($path === 'services') {
|
||||
wp_redirect(home_url('/competences/'), 301);
|
||||
exit;
|
||||
}
|
||||
}, 1);
|
||||
|
||||
/* ── Ressources tierces : rien n'est chargé depuis un CDN externe côté visiteur ── */
|
||||
add_action('wp_enqueue_scripts', function () {
|
||||
wp_dequeue_style('acf-shim-fontawesome'); // icônes Font Awesome de l'admin ACF, inutiles sur le site
|
||||
}, 100);
|
||||
Loading…
Add table
Add a link
Reference in a new issue