منوی پیشخوان، اولین چیزی است که کاربر بعد از ورود به سایت می‌بیند. برای مشتری‌ای که با وردپرس آشنا نیست، منوی شلوغ یا بی‌نظم، تجربهٔ مدیریت را به یک کابوس تبدیل می‌کند. ساخت منوی مدیریتی اختصاصی، مهارتی است که هم در افزونه‌های جدی و هم در سفارشی‌سازی سایت‌های مشتری‌محور به‌کار می‌آید. این مقاله، از ثبت اولین منو تا زیرمنو، ترتیب‌دهی، جداکردن دسترسی و ساخت صفحه‌های داخل هر منو را مرور می‌کند. برای درک پیش‌نیازها، افزونه وردپرس چیست، هوک‌های وردپرس، و توسعهٔ افزونه از صفر را پیش از ادامه ببینید.

منوی پیشخوان در وردپرس

منوی پیشخوان وردپرس از دو بخش تشکیل شده: یک — منوهای اصلی. ستون سمت راست صفحه (در زبان‌های راست‌به‌چپ). هرکدام آیکون و عنوان مشخص دارند و در یک سطح بالایی قرار می‌گیرند. دو — زیرمنوها. با نگه‌داشتن ماوس روی منوی اصلی ظاهر می‌شوند. مثال در ساختار پیش‌فرض: منوی «نوشته‌ها» با زیرمنوهای «همهٔ نوشته‌ها»، «افزودن نوشته»، «دسته‌ها» و «برچسب‌ها». از دید افزونه‌نویسی، سه کاربرد اصلی منوی اختصاصی: یک — صفحهٔ تنظیمات افزونه. محل قرارگیری تنظیمات سفارشی. دو — مدیریت داده‌های اختصاصی. صفحاتی که دادهٔ اختصاصی نشان می‌دهند یا ویرایش می‌کنند. سه — داشبورد اطلاعاتی. نمای کلی آماری یا گزارش‌های اختصاصی. مکانیزم داخلی این ساختار با همان سیستم هوک‌های وردپرس کار می‌کند — راهنمای تفصیلی در هوک‌های وردپرس و تفاوت اکشن و فیلتر.

منوی پیشخوان، ویترین تجربهٔ مدیریت سایت شماست؛ هر افزونه‌ای که در آن اضافه می‌شود، باید جای درستی داشته باشد و اگر جای مناسبی ندارد، دلیلی برای اضافه‌کردنش نیست.

ثبت منوی اصلی با add_menu_page

ساخت منوی اصلی، با تابع add_menu_page انجام می‌شود:

function my_plugin_add_menu() {
    add_menu_page(
        'داشبورد افزونهٔ من',         // عنوان صفحه
        'افزونهٔ من',                 // عنوان منو
        'manage_options',             // capability
        'my-plugin',                  // slug
        'my_plugin_render_dashboard', // callback
        'dashicons-chart-line',       // آیکون
        25                            // موقعیت
    );
}
add_action( 'admin_menu', 'my_plugin_add_menu' );

پارامترهای کلیدی: یک — page_title: عنوانی که در تگ <title> پیشخوان دیده می‌شود. دو — menu_title: متن کوتاهی که در منو نمایش داده می‌شود (باید مختصر باشد). سه — capability: حداقل دسترسی لازم. چهار — menu_slug: شناسهٔ یکتا که در URL استفاده می‌شود. پنج — function: تابعی که محتوای صفحه را رندر می‌کند. شش — icon_url: آیکون منو (Dashicon یا URL). هفت — position: جای منو در ستون. راهنمای دقیق Dashicons در مستندات رسمی وردپرس. یک نکتهٔ مهم: همیشه از هوک admin_menu برای ثبت استفاده کنید، نه init یا هوک‌های دیگر. راهنمای هوک admin_menu در هوک‌های وردپرس.

پس از ثبت منوی اصلی، زیرمنوها اضافه می‌شوند:

function my_plugin_add_submenus() {
    add_submenu_page(
        'my-plugin',                   // parent_slug
        'افزودن مورد جدید',            // page_title
        'افزودن جدید',                 // menu_title
        'manage_options',
        'my-plugin-add-new',
        'my_plugin_render_add_new'
    );

    add_submenu_page(
        'my-plugin',
        'تنظیمات افزونهٔ من',
        'تنظیمات',
        'manage_options',
        'my-plugin-settings',
        'my_plugin_render_settings'
    );
}
add_action( 'admin_menu', 'my_plugin_add_submenus' );

نکته: در زیرمنوها، parent_slug باید با slug منوی اصلی یکی باشد. وردپرس به‌طور خودکار، زیرمنو را به منوی اصلی متصل می‌کند. یک الگوی رایج که در پروژه‌های مختلف دیده‌ام: زیرمنوی اول با همان slug منوی اصلی، به عنوان «داشبورد» یا «نمای کلی» در نظر گرفته می‌شود. این، الگوی استانداردی است که کاربران وردپرس با آن آشنا هستند. مثال:

add_submenu_page(
    'my-plugin',
    'داشبورد افزونهٔ من',
    'داشبورد',
    'manage_options',
    'my-plugin',  // همان slug منوی اصلی
    'my_plugin_render_dashboard'
);

با این الگو، کلیک روی منوی اصلی، صفحهٔ داشبورد را باز می‌کند و زیرمنوهای دیگر در دسترس هستند. راهنمای صفحهٔ تنظیمات در ساخت صفحهٔ تنظیمات اختصاصی.

منو در بخش «تنظیمات»

اگر افزونهٔ شما فقط تنظیمات دارد و صفحهٔ اختصاصی ندارد، از add_options_page استفاده کنید:

function my_plugin_add_settings_page() {
    add_options_page(
        'تنظیمات افزونهٔ من',
        'افزونهٔ من',
        'manage_options',
        'my-plugin-settings',
        'my_plugin_render_settings'
    );
}
add_action( 'admin_menu', 'my_plugin_add_settings_page' );

مزیت این الگو: منوی شما زیر منوی «تنظیمات» پیش‌فرض وردپرس قرار می‌گیرد، بدون اینکه منوی اختصاصی جداگانه‌ای به ستون اضافه شود. برای افزونه‌های سبک، این انتخاب درستی است. تجربه‌های میدانی من نشان داده مشتری‌ها کمتر از منوی جداگانه برای افزونه‌های ساده استقبال می‌کنند؛ زیرمنوی «تنظیمات» برایشان طبیعی‌تر است.

سه تابع مشابه دیگر هم وجود دارد که بسته به نوع صفحه استفاده می‌شوند: یک — add_management_page: برای ابزارهای مدیریتی (زیر منوی «ابزارها»). دو — add_theme_page: برای تنظیمات قالب (زیر منوی «نمایش»). سه — add_users_page: برای صفحه‌های مرتبط با کاربران (زیر منوی «کاربران»). انتخاب درست، پیش‌بینی محل طبیعی منو را برای کاربر ممکن می‌کند. یک قاعدهٔ عملی: اگر افزونهٔ شما در دستهٔ خاصی از عملکرد سایت قرار می‌گیرد، از تابع مربوطه استفاده کنید؛ اگر چند کاربرد دارد و در هیچ‌کدام نمی‌گنجد، add_menu_page انتخاب درستی است.

آیکون و موقعیت منو

دو پارامتر ظاهری که تجربهٔ کاربری را تعیین می‌کنند: یک — آیکون: Dashicon یا URL تصویر SVG. Dashicon‌های پرکاربرد: dashicons-chart-line (نمودار)، dashicons-cart (فروشگاه)، dashicons-groups (کاربران)، dashicons-admin-tools (ابزار)، dashicons-email (ایمیل). راهنمای کامل: developer.wordpress.org/resource/dashicons/. دو — موقعیت: عددی که جای منو را تعیین می‌کند. موقعیت‌های استاندارد وردپرس: 2 (داشبورد)، 4 (جداکننده)، 5 (نوشته‌ها)، 10 (رسانه)، 15 (لینک‌ها)، 20 (برگه‌ها)، 25 (دیدگاه‌ها)، 59 (جداکننده)، 60 (نمایش)، 65 (افزونه‌ها)، 70 (کاربران)، 75 (ابزارها)، 80 (تنظیمات)، 99 (جداکننده). اگر عدد را در یک فاصله بگذارید، منوی شما در همان موقعیت قرار می‌گیرد. اگر عددی دقیقاً برابر با موقعیت موجود باشد، ممکن است تعارض رخ دهد. الگوی توصیه‌شده: عددی که با فاصله از موقعیت‌های پیش‌فرض است (مثلاً 26، 27، 28).

تغییر نام و ترتیب منوهای پیش‌فرض

برای پروژه‌هایی که مشتری با وردپرس آشنا نیست، تغییر نام و ترتیب منوهای پیش‌فرض، تجربهٔ مدیریت را بهتر می‌کند. مثال تغییر نام منوی «نوشته‌ها» به «مقالات»:

function my_rename_admin_menu() {
    global $menu;
    global $submenu;

    // تغییر نام منوی «نوشته‌ها» به «مقالات»
    if ( isset( $menu[5] ) ) {
        $menu[5][0] = 'مقالات';
    }

    // تغییر نام زیرمنوی «افزودن نوشته»
    if ( isset( $submenu['edit.php'][10] ) ) {
        $submenu['edit.php'][10][0] = 'افزودن مقاله';
    }
}
add_action( 'admin_menu', 'my_rename_admin_menu', 999 );

نکته: از اولویت بالا (999) استفاده کنید تا تغییرات بعد از ثبت تمام منوهای دیگر اعمال شود. الگوی کامل در مستندات رسمی وردپرس و ساخت صفحهٔ تنظیمات اختصاصی. یک تذکر: در مشتریانی که با وردپرس آشنا هستند، تغییر نام‌های زیاد ممکن است سردرگمی بسازد. این کار را فقط برای مشتریانی انجام دهید که تجربهٔ کمی با وردپرس دارند.

حذف منوهای غیرلازم

برای پروژه‌هایی که مشتری فقط از بخشی از وردپرس استفاده می‌کند، حذف منوهای غیرلازم، تجربهٔ مدیریت را ساده می‌کند. مثال حذف منوی «ابزارها»:

function my_remove_admin_menu() {
    if ( ! current_user_can( 'manage_options' ) ) {
        remove_menu_page( 'tools.php' );
    }
    remove_menu_page( 'edit-comments.php' ); // حذف منوی دیدگاه‌ها
}
add_action( 'admin_menu', 'my_remove_admin_menu', 999 );

نکتهٔ مهم: در پروژه‌های واقعی، حذف منوها باید با نقش کاربر مرتبط باشد. حذف بی‌دلیل منوها برای کاربر ادمین اصلی، ممکن است بعداً دردسر بسازد. الگوی دقیق در افزونه‌های مدیریت کاربران و توابع نقش و دسترسی. یک قاعده: پیش از حذف هر منوی پیش‌فرض، به کاربرد آن در گردش کار مشتری نگاه کنید. حذف منوی «دیدگاه‌ها» برای سایت‌هایی که کامنت ندارند، انتخاب درستی است؛ حذف منوی «ابزارها» برای سایت‌هایی که از ابزارهای پیش‌فرض استفاده می‌کنند، ممکن است باعث سردرگمی شود.

دسترسی و capability

پارامتر capability در تمام توابع منو، تعیین‌کنندهٔ دسترسی است. کاربردهای رایج: یک — manage_options: فقط ادمین. دو — edit_posts: نویسندگان و بالاتر. سه — edit_pages: ویرایشگران و ادمین. چهار — read: همهٔ کاربران لاگین‌شده. برای منوهای حساس مثل تنظیمات افزونه، همیشه manage_options بگذارید. راهنمای کامل نقش‌ها در توابع نقش و دسترسی وردپرس. یک الگوی حرفه‌ای: به‌جای استفاده از capabilityهای پیش‌فرض وردپرس، برای افزونهٔ اختصاصی capability مخصوص تعریف کنید:

// هنگام فعال‌سازی افزونه
$role = get_role( 'administrator' );
$role->add_cap( 'manage_my_plugin' );

// در ثبت منو
add_menu_page(
    'داشبورد',
    'افزونهٔ من',
    'manage_my_plugin',  // capability اختصاصی
    'my-plugin',
    'my_plugin_render'
);

با این الگو، می‌توانید بعداً نقش‌های دقیق‌تری تعریف کنید، بدون تغییر کد افزونه.

صفحهٔ داخلی منو و رندر

تابع callback، محتوای صفحهٔ منو را رندر می‌کند. الگوی استاندارد:

function my_plugin_render_dashboard() {
    if ( ! current_user_can( 'manage_options' ) ) {
        wp_die( esc_html__( 'شما اجازهٔ دسترسی به این صفحه را ندارید.', 'my-plugin' ) );
    }

    ?>
    <div class="wrap">
        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
        <div class="my-plugin-content">
            <p>محتوای داشبورد افزونه.</p>
        </div>
    </div>
    <?php
}

سه نکته: یک — current_user_can: حتی با وجود capability در ثبت، این بررسی در متد نیز الزامی است. دو — wrap class: حاشیه و فاصله‌گذاری استاندارد پیشخوان را می‌دهد. سه — get_admin_page_title: عنوان ثبت‌شده را بازمی‌گرداند. راهنمای امنیت در PHP امن در وردپرس، امنیت وردپرس برای مبتدیان، و امنیت پروژهٔ وردپرس. برای استفاده از فایل‌های قالب جداگانه در پیشخوان، از الگوی زیر استفاده کنید:

function my_plugin_render_dashboard() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    include plugin_dir_path( __FILE__ ) . 'admin/views/dashboard.php';
}

این الگو، فایل PHP را از منطق جدا می‌کند و نگهداری را ساده‌تر می‌سازد. ساختار پوشهٔ admin در ساختار فایل‌های افزونهٔ استاندارد آمده است.

ساختار کلاس‌محور منو

در افزونه‌های جدی، منو در یک کلاس جداگانه تعریف می‌شود:

class My_Plugin_Admin_Menu {

    public static function init() {
        add_action( 'admin_menu', array( __CLASS__, 'register' ) );
    }

    public static function register() {
        add_menu_page(
            'داشبورد',
            'افزونهٔ من',
            'manage_options',
            'my-plugin',
            array( __CLASS__, 'render_dashboard' ),
            'dashicons-chart-line',
            26
        );

        add_submenu_page(
            'my-plugin',
            'تنظیمات',
            'تنظیمات',
            'manage_options',
            'my-plugin-settings',
            array( __CLASS__, 'render_settings' )
        );
    }

    public static function render_dashboard() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        include plugin_dir_path( __FILE__ ) . '../admin/views/dashboard.php';
    }

    public static function render_settings() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        include plugin_dir_path( __FILE__ ) . '../admin/views/settings.php';
    }
}
My_Plugin_Admin_Menu::init();

مزیت: تمام منو در یک نقطه، نام‌گذاری بدون تعارض، و امکان تست. تجربه‌های میدانی من در این مورد: در پروژه‌ای با پنج منوی اختصاصی، انتقال به این ساختار، زمان دیباگ را نصف کرد. الگوی کامل در کدنویسی اختصاصی افزونه و استانداردهای کدنویسی وردپرس. در پروژه‌های بزرگ، این ساختار، تفاوت بین نگهداری ساده و پیچیده را می‌سازد. یک نکتهٔ ساختاری: در همین الگو، می‌توانید از Service Container یا Registry Pattern برای مدیریت بهتر منوها استفاده کنید. راهنمای بیشتر در بهینه‌سازی کد وردپرس و تست و دیباگ پروژه‌های وردپرس.

امنیت و امن‌سازی صفحه‌ها

چهار قاعدهٔ الزامی برای امنیت صفحه‌های پیشخوان: یک — capability در ثبت منو. الزامی است. دو — current_user_can در callback. حتی اگر capability در ثبت باشد، بررسی دوم در callback، لایهٔ امنیتی اضافه است. سه — nonce در فرم‌ها. برای هر فرم و درخواست AJAX:

// در فرم
<?php wp_nonce_field( 'my_plugin_save', 'my_plugin_nonce' ); ?>

// در پردازش
if ( ! isset( $_POST['my_plugin_nonce'] ) ) {
    return;
}
if ( ! wp_verify_nonce( $_POST['my_plugin_nonce'], 'my_plugin_save' ) ) {
    return;
}

چهار — escape خروجی. در رندر صفحه، تمام داده‌ها با esc_html، esc_attr، esc_url خروجی داده شوند. راهنمای کامل در نانس وردپرس و امنیت فرم، پاک‌سازی داده‌ها، و اعتبارسنجی داده‌ها. یک آسیب‌پذیری شایع که در پرونده‌های امنیتی دیده‌ام: صفحه‌ای که فقط به‌خاطر استفادهٔ داخلی، بررسی current_user_can را حذف کرده بود. این صفحه، از دید مهاجم، دروازهٔ ورود مناسبی است. هیچ صفحه‌ای از بررسی دسترسی، معاف نیست.

اشتباهات رایج

ساخت منوی مدیریتی در وردپرس، مسیر روشنی دارد: ثبت منوی اصلی با add_menu_page، افزودن زیرمنوها با add_submenu_page، انتخاب صحیح موقعیت و آیکون، مدیریت capability، و رعایت امنیت در هر دو نقطهٔ ثبت و رندر. در پروژه‌های مشتری‌محور، تغییر نام و ترتیب منوها می‌تواند تجربهٔ مدیریت را به‌شدت بهتر کند. اگر امروز یک کار در این مسیر انجام می‌دهید: یک منوی اختصاصی ساده در افزونهٔ فعلی خود اضافه کنید که یک صفحهٔ «دربارهٔ افزونه» را باز کند. همین اولین تجربه، پایه‌ای برای ساخت صفحه‌های پیچیده‌تر می‌شود. اگر تجربه‌ای از ساخت منوی اختصاصی دارید — به‌ویژه در پروژه‌هایی که مدیریت کاربران غیرفنی مطرح بوده — در دیدگاه‌ها بنویسید؛ همان گزارش‌های واقعی، این راهنما را دقیق‌تر می‌کند. 🧩