WordPress Admin Paneline Özel Menü Sayfası Ekleme

WordPress ile çalışan her geliştirici ya da site yöneticisi bir noktada şunu fark eder: Varsayılan admin menüsü her zaman ihtiyacınıza göre şekillenmez. Belki bir müşteriye özel bir raporlama sayfası sunmak istiyorsunuz, belki WooCommerce siparişleri için özel bir dashboard hazırlıyorsunuz ya da sadece sık kullandığınız ayarları tek bir yerde toplamak istiyorsunuz. İşte tam burada WordPress’in add_menu_page() ve add_submenu_page() fonksiyonları devreye giriyor. Bu yazıda, functions.php dosyası üzerinden admin paneline nasıl özel menü sayfaları ekleyeceğinizi, bu sayfaları nasıl yetkilendireceğinizi ve gerçek dünya senaryolarında nasıl kullanabileceğinizi adım adım ele alacağız.

Temel Kavramlar ve Neden functions.php Kullanıyoruz?

WordPress admin menü sistemi, hook tabanlı bir yapı üzerine kurulu. Yani menü eklemek için doğru hook’u yakalamak gerekiyor. admin_menu hook’u, bu iş için biçilmiş kaftan. Bir eklenti yazıyor olsanız eklenti dosyasına, tema özelleştirmesi yapıyor olsanız functions.php dosyasına bu kodu ekleyebilirsiniz.

Peki neden functions.php? Çünkü:

  • Eklenti kurmadan hızlıca özelleştirme yapmak istiyorsunuz
  • Müşteriye teslim edeceğiniz tema içinde her şeyin bir arada olmasını istiyorsunuz
  • Basit yönetim araçları için ayrı bir eklenti oluşturmak gereksiz geliyor

Ama şunu da belirtmek lazım: Eğer tema değişirse bu kodlar gider. Bu yüzden “mu-plugins” klasörünü ya da özel bir eklenti kullanmak daha sağlıklı bir yaklaşım. Ancak fonksiyon mantığı her iki yerde de aynı.

Temel Menü Sayfası Ekleme

WordPress’te üst düzey (top-level) bir menü sayfası eklemek için add_menu_page() fonksiyonunu kullanıyoruz. Önce basit bir örnekle başlayalım:

<?php
// Admin menüsüne özel sayfa ekle
function ozel_admin_menusu_ekle() {
    add_menu_page(
        'Özel Yönetim Paneli',   // Sayfa başlığı (title tag)
        'Özel Panel',             // Menüde görünen isim
        'manage_options',         // Yetki (capability)
        'ozel-panel',             // Slug (URL için)
        'ozel_panel_icerik',      // İçeriği render eden fonksiyon
        'dashicons-chart-bar',    // Menü ikonu
        25                        // Menüdeki pozisyon
    );
}
add_action( 'admin_menu', 'ozel_admin_menusu_ekle' );

// Sayfa içeriği
function ozel_panel_icerik() {
    echo '<div class="wrap">';
    echo '<h1>Özel Yönetim Paneli</h1>';
    echo '<p>Buraya istediğiniz içeriği ekleyebilirsiniz.</p>';
    echo '</div>';
}

Bu kadar. Sayfayı kaydedin, admin panelini yenileyin ve sol menüde “Özel Panel” seçeneğini göreceksiniz. Ama bu sadece başlangıç.

Parametreleri Anlamak

add_menu_page() fonksiyonunun parametrelerini iyi anlamak önemli çünkü küçük bir hata yetkilendirme sorunlarına ya da görünmez menülere yol açabilir:

  • page_title: Tarayıcı sekmesinde görünen başlık
  • menu_title: Sol menüde görünen kısa isim
  • capability: Hangi yetkiye sahip kullanıcıların bu menüyü göreceği. manage_options sadece adminler içindir. edit_posts editörler için de çalışır.
  • menu_slug: Sayfanın URL’sini belirleyen benzersiz tanımlayıcı. admin.php?page=ozel-panel gibi
  • callback: Sayfa içeriğini üreten PHP fonksiyonu
  • icon_url: Dashicons adı ya da özel SVG/URL
  • position: Menüdeki sıra. 2 Dashboard’ın hemen altıdır, 100 en alta koyar

Alt Menü Sayfaları Eklemek

Çoğu durumda tek bir sayfa yetmez. Bir üst menü altında birden fazla sekme ya da bölüm oluşturmak istersiniz. Bunun için add_submenu_page() kullanıyoruz:

<?php
function ozel_menu_ve_alt_menuler() {
    // Ana menü
    add_menu_page(
        'Mağaza Raporları',
        'Mağaza Raporları',
        'manage_options',
        'magaza-raporlari',
        'magaza_ana_sayfa',
        'dashicons-store',
        58
    );

    // Alt menü 1 - Ana sayfanın kendisi
    add_submenu_page(
        'magaza-raporlari',       // Parent slug
        'Genel Bakış',
        'Genel Bakış',
        'manage_options',
        'magaza-raporlari',       // Aynı slug = ana sayfa
        'magaza_ana_sayfa'
    );

    // Alt menü 2
    add_submenu_page(
        'magaza-raporlari',
        'Satış Raporları',
        'Satış Raporları',
        'manage_options',
        'magaza-satis-raporlari',
        'magaza_satis_sayfasi'
    );

    // Alt menü 3 - Sadece editörler görebilir
    add_submenu_page(
        'magaza-raporlari',
        'İçerik İstatistikleri',
        'İçerik İstatistikleri',
        'edit_posts',
        'magaza-icerik-istatistik',
        'magaza_icerik_sayfasi'
    );
}
add_action( 'admin_menu', 'ozel_menu_ve_alt_menuler' );

function magaza_ana_sayfa() {
    echo '<div class="wrap"><h1>Mağaza Genel Bakış</h1></div>';
}

function magaza_satis_sayfasi() {
    echo '<div class="wrap"><h1>Satış Raporları</h1></div>';
}

function magaza_icerik_sayfasi() {
    echo '<div class="wrap"><h1>İçerik İstatistikleri</h1></div>';
}

Burada dikkat etmeniz gereken bir nokta var: İlk add_submenu_page() çağrısında parent slug ile child slug aynı olduğunda, bu alt menü öğesi ana menünün etiketi gibi davranır. Eğer farklı verirseniz, menüde hem ana başlık hem de ayrı bir ilk alt menü görürsünüz ki bu genellikle istemediğiniz bir durumdur.

Gerçek Dünya Senaryosu: WooCommerce Sipariş Özet Paneli

Diyelim ki bir e-ticaret sitesi yönetiyorsunuz. Müşteri her sabah sipariş durumunu görmek istiyor ama WooCommerce’in tüm karmaşıklığına maruz kalmasını istemiyorsunuz. Basit, temiz bir özet sayfası hazırlayalım:

<?php
function woo_ozet_menu_ekle() {
    add_menu_page(
        'Sipariş Özeti',
        'Sipariş Özeti',
        'manage_woocommerce',
        'woo-siparis-ozeti',
        'woo_siparis_ozeti_render',
        'dashicons-clipboard',
        3
    );
}
add_action( 'admin_menu', 'woo_ozet_menu_ekle' );

function woo_siparis_ozeti_render() {
    // WooCommerce aktif değilse uyar
    if ( ! function_exists( 'wc_get_orders' ) ) {
        echo '<div class="wrap"><p>WooCommerce aktif değil.</p></div>';
        return;
    }

    // Son 7 günün siparişleri
    $args = array(
        'status'     => array( 'wc-processing', 'wc-on-hold' ),
        'limit'      => -1,
        'date_after' => date( 'Y-m-d', strtotime( '-7 days' ) ),
    );

    $siparisler = wc_get_orders( $args );
    $toplam_tutar = 0;

    foreach ( $siparisler as $siparis ) {
        $toplam_tutar += $siparis->get_total();
    }

    echo '<div class="wrap">';
    echo '<h1>Son 7 Günün Siparişleri</h1>';
    echo '<p><strong>Bekleyen/İşlemdeki Sipariş Sayısı:</strong> ' . count( $siparisler ) . '</p>';
    echo '<p><strong>Toplam Tutar:</strong> ' . wc_price( $toplam_tutar ) . '</p>';
    echo '<hr>';

    foreach ( $siparisler as $siparis ) {
        echo '<p>';
        echo '<strong>Sipariş #' . $siparis->get_id() . '</strong> - ';
        echo $siparis->get_billing_first_name() . ' ' . $siparis->get_billing_last_name() . ' - ';
        echo wc_price( $siparis->get_total() ) . ' - ';
        echo ucfirst( $siparis->get_status() );
        echo '</p>';
    }

    echo '</div>';
}

Bu sayfa sayesinde müşteri sabah işe geldiğinde WordPress admin’e giriyor, sol menüde “Sipariş Özeti” seçiyor ve o gün için bekleyen siparişleri anında görüyor. Sade, hızlı, işlevsel.

Mevcut WordPress Menülerine Alt Sayfa Eklemek

Bazen sıfırdan menü oluşturmak yerine, var olan menülere alt sayfa eklemek daha mantıklı olabilir. Örneğin “Araçlar” menüsüne özel bir araç sayfası ekleyelim:

<?php
function araclar_menuye_ozel_ekle() {
    add_submenu_page(
        'tools.php',              // Parent: Araçlar menüsü
        'Site Temizleme Aracı',
        'Site Temizleme',
        'manage_options',
        'site-temizleme-araci',
        'site_temizleme_render'
    );
}
add_action( 'admin_menu', 'araclar_menuye_ozel_ekle' );

function site_temizleme_render() {
    // Nonce güvenlik kontrolü
    $nonce_action = 'site_temizleme_islemi';

    if ( isset( $_POST['temizle'] ) && check_admin_referer( $nonce_action ) ) {
        // Revizyon temizleme işlemi
        global $wpdb;
        $silinen = $wpdb->query(
            "DELETE FROM $wpdb->posts WHERE post_type = 'revision'"
        );
        echo '<div class="notice notice-success"><p>' . $silinen . ' revizyon silindi.</p></div>';
    }

    echo '<div class="wrap">';
    echo '<h1>Site Temizleme Aracı</h1>';
    echo '<p>Bu araç gereksiz revizyonları, taslakları ve geçici verileri temizler.</p>';
    echo '<form method="post">';
    wp_nonce_field( $nonce_action );
    echo '<input type="submit" name="temizle" class="button button-primary" value="Revizyonları Temizle">';
    echo '</form>';
    echo '</div>';
}

Dikkat ettiniz mi? Form işlemlerinde mutlaka wp_nonce_field() ve check_admin_referer() kullanıyoruz. Bu, CSRF saldırılarına karşı temel bir önlem. Admin sayfalarında form işlerken bunu atlamak güvenlik açığı yaratır.

Yetkilendirme ile Rol Bazlı Menü Yönetimi

Farklı roller için farklı menüler göstermek yaygın bir ihtiyaç. Örneğin shop manager rolündeki kullanıcılar bazı raporları görebilsin ama site ayarlarına dokunamasın:

<?php
function rol_bazli_menu_ekle() {
    $kullanici = wp_get_current_user();

    // Sadece adminler için
    if ( current_user_can( 'manage_options' ) ) {
        add_menu_page(
            'Admin Araçları',
            'Admin Araçları',
            'manage_options',
            'admin-araclar',
            'admin_araclar_render',
            'dashicons-admin-tools',
            99
        );
    }

    // Shop manager ve üzeri için WooCommerce rapor sayfası
    if ( current_user_can( 'manage_woocommerce' ) ) {
        add_menu_page(
            'Satış Panosu',
            'Satış Panosu',
            'manage_woocommerce',
            'satis-panosu',
            'satis_panosu_render',
            'dashicons-analytics',
            4
        );
    }

    // Editörler için içerik araçları
    if ( current_user_can( 'edit_others_posts' ) ) {
        add_submenu_page(
            'edit.php',
            'İçerik Planlama',
            'İçerik Planlama',
            'edit_others_posts',
            'icerik-planlama',
            'icerik_planlama_render'
        );
    }
}
add_action( 'admin_menu', 'rol_bazli_menu_ekle' );

function admin_araclar_render() {
    echo '<div class="wrap"><h1>Admin Araçları</h1><p>Yalnızca site yöneticileri bu sayfayı görüntüleyebilir.</p></div>';
}

function satis_panosu_render() {
    echo '<div class="wrap"><h1>Satış Panosu</h1><p>Satış istatistikleri burada görünecek.</p></div>';
}

function icerik_planlama_render() {
    echo '<div class="wrap"><h1>İçerik Planlama</h1><p>Editörler için içerik takvimi.</p></div>';
}

Admin Sayfasına CSS ve JavaScript Eklemek

Özel admin sayfanızda tablo, grafik ya da özel stil kullanmak istiyorsanız, admin_enqueue_scripts hook’u size yardımcı olacak. Ama dikkat: CSS ve JS’yi sadece kendi sayfanızda yükleyin, tüm admin paneline değil:

<?php
function ozel_admin_sayfa_varliklari( $hook ) {
    // Sadece bizim sayfamızda yükle
    if ( 'toplevel_page_magaza-raporlari' !== $hook ) {
        return;
    }

    wp_enqueue_style(
        'ozel-admin-stil',
        get_template_directory_uri() . '/assets/css/admin-panel.css',
        array(),
        '1.0.0'
    );

    wp_enqueue_script(
        'ozel-admin-script',
        get_template_directory_uri() . '/assets/js/admin-panel.js',
        array( 'jquery' ),
        '1.0.0',
        true
    );

    // PHP'den JS'ye veri aktar
    wp_localize_script(
        'ozel-admin-script',
        'ozelAdminData',
        array(
            'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
            'nonce'     => wp_create_nonce( 'ozel_ajax_nonce' ),
            'siparisler' => wc_get_orders( array( 'limit' => 5 ) ),
        )
    );
}
add_action( 'admin_enqueue_scripts', 'ozel_admin_sayfa_varliklari' );

Hook adına dikkat edin. Üst düzey sayfalar için toplevel_page_{slug} formatında oluyor. Alt menü sayfaları için ise {parent-slug}_page_{child-slug} formatı kullanılıyor. Örneğin tools.php altına eklediğiniz bir sayfa için tools_page_site-temizleme-araci olur.

Ayarlar API’si ile Entegrasyon

Oluşturduğunuz sayfalara form ekleyip veritabanına veri kaydetmek istediğinizde WordPress’in Settings API’sini kullanmak en güvenli yol:

<?php
// Ayarları kaydet
function ozel_ayarlar_kayit() {
    register_setting(
        'ozel_ayarlar_grubu',    // Option group
        'ozel_site_ayarlari',    // Option adı
        array(
            'sanitize_callback' => 'ozel_ayarlar_temizle',
        )
    );

    add_settings_section(
        'ozel_genel_bolum',
        'Genel Ayarlar',
        'ozel_bolum_aciklama',
        'ozel-ayarlar-sayfasi'
    );

    add_settings_field(
        'sirket_adi',
        'Şirket Adı',
        'sirket_adi_render',
        'ozel-ayarlar-sayfasi',
        'ozel_genel_bolum'
    );
}
add_action( 'admin_init', 'ozel_ayarlar_kayit' );

function ozel_ayarlar_temizle( $input ) {
    $output = array();
    if ( isset( $input['sirket_adi'] ) ) {
        $output['sirket_adi'] = sanitize_text_field( $input['sirket_adi'] );
    }
    return $output;
}

function ozel_bolum_aciklama() {
    echo '<p>Site genelinde kullanılacak genel ayarlar.</p>';
}

function sirket_adi_render() {
    $ayarlar = get_option( 'ozel_site_ayarlari', array() );
    $deger = isset( $ayarlar['sirket_adi'] ) ? $ayarlar['sirket_adi'] : '';
    echo '<input type="text" name="ozel_site_ayarlari[sirket_adi]" value="' . esc_attr( $deger ) . '" class="regular-text">';
}

function ozel_ayarlar_sayfasi_render() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( 'Yetersiz yetki.' );
    }

    echo '<div class="wrap">';
    echo '<h1>Özel Site Ayarları</h1>';
    echo '<form method="post" action="options.php">';
    settings_fields( 'ozel_ayarlar_grubu' );
    do_settings_sections( 'ozel-ayarlar-sayfasi' );
    submit_button( 'Ayarları Kaydet' );
    echo '</form>';
    echo '</div>';
}

Settings API kullanmanın en büyük avantajı WordPress’in güvenlik ve doğrulama mekanizmalarını otomatik olarak devreye sokması. options.php action’ı nonce kontrolünü, izin kontrolünü ve veri sanitizasyonunu sizin yerinize hallediyor.

Menü Sırasını ve İkonları Yönetmek

Menü pozisyonları konusunda çakışmalar yaşanabilir. İşte varsayılan WordPress menü pozisyonları:

  • 2: Dashboard
  • 4: Separator
  • 5: Yazılar
  • 10: Medya
  • 15: Linkler
  • 20: Sayfalar
  • 25: Yorumlar
  • 59: Separator
  • 60: Görünüm
  • 65: Eklentiler
  • 70: Kullanıcılar
  • 75: Araçlar
  • 80: Ayarlar
  • 99: Separator

Bu pozisyonları kullanmak yerine, çakışmayı önlemek için ondalıklı bir sayı (örneğin 25.3) kullanabilirsiniz. Dashicons için WordPress’in icon listesinden seçim yapabilir ya da özel SVG de kullanabilirsiniz:

<?php
// SVG ikon kullanımı
function svg_ikonlu_menu_ekle() {
    $svg_ikon = 'data:image/svg+xml;base64,' . base64_encode(
        '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
            <path fill="#a7aaad" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 
            10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z"/>
        </svg>'
    );

    add_menu_page(
        'Video Yönetimi',
        'Video Yönetimi',
        'manage_options',
        'video-yonetimi',
        'video_yonetim_render',
        $svg_ikon,
        26
    );
}
add_action( 'admin_menu', 'svg_ikonlu_menu_ekle' );

function video_yonetim_render() {
    echo '<div class="wrap"><h1>Video Yönetimi</h1></div>';
}

Güvenlik Kontrolleri ve En İyi Pratikler

Admin sayfalarınızda her zaman şu güvenlik kurallarına uyun:

  • Yetki kontrolü yapın: Her callback fonksiyonunun başına current_user_can() ekleyin
  • Nonce kullanın: Form gönderimlerinde mutlaka nonce doğrulaması yapın
  • Veriyi sanitize edin: $_GET ve $_POST verilerini doğrudan kullanmayın
  • Çıktıyı escape edin: Veritabanından ya da kullanıcıdan gelen veriyi ekrana basarken esc_html(), esc_attr(), esc_url() kullanın
  • Direkt erişimi engelleyin: PHP dosyalarının başına defined('ABSPATH') || die; ekleyin
<?php
// Güvenli bir admin sayfa callback örneği
function guvenli_admin_sayfa_render() {
    // 1. Yetki kontrolü
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( __( 'Bu sayfayı görüntüleme yetkiniz yok.', 'textdomain' ) );
    }

    // 2. GET parametresi güvenli al
    $sekme = isset( $_GET['sekme'] ) ? sanitize_key( $_GET['sekme'] ) : 'genel';
    $izinli_sekmeler = array( 'genel', 'gelismis', 'raporlar' );

    if ( ! in_array( $sekme, $izinli_sekmeler, true ) ) {
        $sekme = 'genel';
    }

    // 3. İşlem varsa nonce kontrol et
    if ( isset( $_POST['gonder'] ) ) {
        check_admin_referer( 'guvenli_form_islemi' );
        $metin = sanitize_text_field( $_POST['metin'] ?? '' );
        update_option( 'ozel_metin_ayari', $metin );
        echo '<div class="notice notice-success"><p>Kaydedildi.</p></div>';
    }

    // 4. Çıktıyı escape ederek bas
    $kayitli_metin = get_option( 'ozel_metin_ayari', '' );

    echo '<div class="wrap">';
    echo '<h1>' . esc_html__( 'Güvenli Admin Sayfası', 'textdomain' ) . '</h1>';
    echo '<form method="post">';
    wp_nonce_field( 'guvenli_form_islemi' );
    echo '<input type="text" name="metin" value="' . esc_attr( $kayitli_metin ) . '" class="regular-text">';
    echo '<input type="submit" name="gonder" class="button button-primary" value="Kaydet">';
    echo '</form>';
    echo '</div>';
}

Sonuç

WordPress admin paneline özel menü sayfası eklemek kulağa karmaşık gelebilir ama gördüğünüz gibi mantığı oldukça basit. admin_menu hook’unu yakala, add_menu_page() ya da add_submenu_page() ile sayfanı tanımla, callback fonksiyonunda içeriği render et.

Ama asıl önemli olan kısım güvenlik. Her admin sayfası potansiyel bir saldırı yüzeyidir. Yetki kontrolü, nonce doğrulama ve veri sanitizasyonu olmadan yazdığınız her satır kod bir güvenlik açığına dönüşebilir. Bu yüzden örneklerdeki güvenlik katmanlarını şablon olarak benimsemenizi şiddetle tavsiye ediyorum.

Gerçek projelerde bu yapıyı kullanarak müşteri özel raporlama panelleri, içerik yönetim araçları, WooCommerce dashboard eklentileri ve hatta tam özellikli yönetim uygulamaları geliştirebilirsiniz. functions.php bu işin başlangıç noktası olsa da ölçeklenen projelerde mutlaka özel bir eklenti yapısına geçin. Hem bakım kolaylaşır hem de tema değişikliklerinden etkilenmezsiniz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir