Kiedy oddajesz stronę klientowi, domyślny panel administracyjny często przytłacza. Pełno w nim powiadomień o promocjach wtyczek, mylących pozycji jak „Komentarze” (gdy komentarze są wyłączone) i technicznego żargonu, którego redaktor nie potrzebuje na co dzień.
Generyczny kokpit mówi „zainstalowałem motyw”. Dostosowany kokpit mówi „dostałeś narzędzie do pracy”.
W tym przewodniku przechodzę przez praktyczną customizację panelu WordPress: czyszczenie menu pod role, własne role i uprawnienia, strony wsparcia i ustawień, ładowanie CSS i JS, schemat kolorów, toolbar, widżety kokpitu oraz ekran logowania. Kod jest celowo krótki, bo większość problemów w produkcji bierze się z ukrywania bez kontroli uprawnień, a nie z braku frameworka opcji. Oficjalne odniesienia do ładowania assetów i budowania menu to admin_enqueue_scripts i admin_menu.

Dlaczego customizacja admina obniża liczbę ticketów
Po oddaniu sklepu WooCommerce z Elementorem klient dostaje kilkadziesiąt pozycji menu, z których używa trzech. Reszta generuje pytania: „czy mogę kliknąć Motywy?”, „co robi ACF?”, „dlaczego widzę WooCommerce > Status?”.
W jednym wdrożeniu dla redakcji lokalnej (kilku edytorów, jeden administrator agencji) po usunięciu Tools, Settings i edytorów plików z motywu liczba maili „coś zepsułem” spadła w pierwszym miesiącu mniej więcej o połowę. Nie dlatego, że panel stał się „ładniejszy”, tylko dlatego, że niebezpieczne ścieżki zniknęły z pola widzenia osób bez manage_options.
Drugi efekt: onboarding. Zamiast godzinnego szkolenia „nie klikaj tu”, pokazujesz jedną stronę „Wsparcie” z linkiem do dokumentacji, numerem osoby kontaktowej i checklistą publikacji. To jest white-label w sensie produktowym, nie tylko wymiana logo.
Customizacja admina we wtyczce must-use zamiast w functions.php
Customizację admina trzymaj we wtyczce, nie w functions.php motywu. Motyw zmienia się przy redesignie; role, menu i widżety mają zostać. Must-use plugin (wp-content/mu-plugins/) sprawdza się, gdy klient nie może przypadkiem wyłączyć paczki z poziomu listy wtyczek.
Typowa struktura:
wp-content/mu-plugins/
wppoland-admin-ux.php # plik startowy (ładuje katalog)
wppoland-admin-ux/
menu.php
enqueue.php
capabilities.php
dashboard.phpPlik startowy w katalogu głównym mu-plugins/ ładuje się automatycznie. Dołącza tylko pliki z podkatalogu. Zaleta: kod działa zawsze, niezależnie od aktywnych zwykłych wtyczek, a cały katalog wersjonujesz bez ryzyka, że ktoś go wyłączy na ekranie Wtyczki.
<?php
/**
* Plugin Name: WPPoland Admin UX
* Description: Porządek w menu, ładowanie assetów i strony wsparcia dla ról klienta.
* Author: wppoland.com
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$dir = __DIR__ . '/wppoland-admin-ux';
require_once $dir . '/menu.php';
require_once $dir . '/enqueue.php';
require_once $dir . '/capabilities.php';
require_once $dir . '/dashboard.php';Testuj na koncie z rolą edytora, nie tylko jako administrator. Najczęstszy błąd: deweloper loguje się jako admin, widzi „czysty” panel (bo cleanup się nie odpala dla manage_options) i ogłasza sukces, a redaktor nadal widzi ACF i Tools.
Jeśli projekt ma już wtyczkę agencji z autoloadingiem Composera, plik startowy must-use może po prostu ją wczytać. Sens zostaje ten sam: UX panelu nie może umrzeć, gdy przy redesignie ktoś wymieni motyw potomny Elementora.
Trzymaj pliki małe. Jeden plik na menu, jeden na assety, jeden na blokady. Kiedy kolejny deweloper będzie szukał, dlaczego zniknęło menu ACF, nie powinien przekopywać functions.php na 800 linii.
Na stagingu warto sprawdzić też mobilny wp-admin. Toolbar i własne menu potrafią rozjechać się na wąskich ekranach, zwłaszcza gdy dokładasz długie etykiety po polsku.
Czym wtyczka must-use różni się od zwykłej wtyczki
Podręcznik zaawansowanej administracji wymienia różnice, a kilka z nich zmienia sposób pisania kodu panelu:
- Wtyczki must-use są włączone na wszystkich witrynach instalacji i nie da się ich wyłączyć z wp-admin.
- PHP ładuje je w kolejności alfabetycznej, przed zwykłymi wtyczkami.
- Pliki w podkatalogach nie są dołączane automatycznie. Dlatego struktura powyżej potrzebuje pliku startowego w katalogu głównym.
- Nie pojawiają się na domyślnej liście na ekranie Wtyczki, tylko na osobnej liście Must-Use, którą widzą wyłącznie osoby z dostępem do Wtyczek.
- Nie trafiają do powiadomień o aktualizacjach, więc nowe wersje śledzisz sam.
- Hooki aktywacji nie są wykonywane dla wtyczek w katalogu must-use.
Ostatni punkt zaskakuje najczęściej. Kod, który zwykła wtyczka umieściłaby w register_activation_hook, na przykład tworzenie ról, we wtyczce must-use potrzebuje własnego sprawdzenia wersji. Kolejne sekcje pokazują ten wzorzec.
Jak sprawdzać capabilities przed zmianą menu admina
remove_menu_page i add_menu_page to API widoczności. Nie zastępują kontroli dostępu. Użytkownik, który zna URL, nadal otworzy tools.php albo options-general.php, jeśli capability na to pozwala.
Zasady, które trzymają się w produkcji:
- Sprawdź
current_user_can( 'manage_options' )(albo bardziej szczegółową capability), zanim ukryjesz menu rolom klienta. - Nigdy nie ukrywaj menu przed osobą, która ma debugować stronę. Administrator bez widocznego menu kosztuje więcej czasu, niż oszczędza.
- Gdy naprawdę trzeba coś zablokować, użyj
admin_initalbomap_meta_capi zwróćwp_die()lub przekierowanie z wyjaśnieniem, a nie samodisplay: nonew CSS.
W redakcjach, w których z jednej instalacji korzystają freelancerzy i etatowi redaktorzy, opłaca się osobna rola z edit_posts i upload_files, bez install_plugins i edit_themes. Wtedy porządek w menu jest odbiciem tej roli, a nie długą listą wyjątków po ID użytkownika. get_role( 'editor' )->remove_cap( '...' ) należy do jednorazowej migracji albo komendy CLI, nie do hooka, który odpala się na każdej stronie panelu.
/**
* Zablokuj bezpośredni dostęp URL do Narzędzi dla nie-administratorów.
*/
function wppoland_block_tools_screen(): void {
if ( ! is_admin() || wp_doing_ajax() ) {
return;
}
if ( current_user_can( 'manage_options' ) ) {
return;
}
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( ! $screen || 'tools' !== $screen->id ) {
return;
}
wp_die(
esc_html__( 'Nie masz dostępu do tego ekranu.', 'wppoland-admin-ux' ),
esc_html__( 'Brak dostępu', 'wppoland-admin-ux' ),
[ 'response' => 403 ]
);
}
add_action( 'current_screen', 'wppoland_block_tools_screen' );Nazwy capability są kontraktem. ID użytkowników się zmieniają, a shop manager zachowuje manage_woocommerce. Buduj reguły wokół capability, nie wokół użytkownika o ID 12.
Własna rola klienta z add_role i map_meta_cap
Role są zapisane w bazie danych, nie w kodzie. Dokumentacja add_role podaje, że funkcja kończy działanie i zwraca null, jeśli rola już istnieje. Zmieniona lista capability nie zadziała więc, dopóki rola nie zostanie usunięta i dodana ponownie. Bez sprawdzenia wersji działoby się to przy każdym żądaniu, a ponieważ wtyczki must-use nie mają hooka aktywacji, zapisana wartość wersji jest najprostszym zabezpieczeniem.
function wppoland_register_client_role(): void {
if ( '2' === get_option( 'wppoland_roles_version' ) ) {
return;
}
remove_role( 'client_manager' );
add_role(
'client_manager',
'Redakcja klienta',
[
'read' => true,
'edit_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'delete_posts' => true,
'edit_pages' => true,
'edit_published_pages' => true,
'edit_others_pages' => true,
'publish_pages' => true,
'upload_files' => true,
'moderate_comments' => true,
]
);
update_option( 'wppoland_roles_version', '2' );
}
add_action( 'init', 'wppoland_register_client_role' );Capability, których nie ma na liście, rola po prostu nie dostaje. To bezpieczniejsze niż jawne manage_options => false, bo zapomniany klucz niczego wtedy nie otwiera. Gdy lista się zmienia, podbijasz wartość wersji.
Niektórych stron klient nie powinien edytować, nawet jeśli rola ma edit_others_pages, zwykle strony głównej. Filtr map_meta_cap tłumaczy meta-capability, takie jak edit_post, na capability podstawowe, i tam możesz dopisać do_not_allow dla jednej strony:
function wppoland_protect_front_page( array $caps, string $cap, int $user_id, array $args ): array {
if ( ! in_array( $cap, [ 'edit_post', 'delete_post' ], true ) || empty( $args[0] ) ) {
return $caps;
}
$front_page = (int) get_option( 'page_on_front' );
if ( $front_page && (int) $args[0] === $front_page && ! user_can( $user_id, 'manage_options' ) ) {
$caps[] = 'do_not_allow';
}
return $caps;
}
add_filter( 'map_meta_cap', 'wppoland_protect_front_page', 10, 4 );Przewaga nad ukryciem linku „Edytuj”: reguła działa wszędzie, na liście stron, w edytorze blokowym, w REST API i w szybkiej edycji.
Jak posprzątać menu administratora WordPress
Pierwszym widocznym krokiem jest remove_menu_page. Większość klientów nie musi widzieć „Narzędzi” ani „Ustawień”.
Dobra praktyka: sprawdzaj capabilities, nie ID użytkownika. Nigdy nie ukrywaj menu przed administratorami z pełnymi uprawnieniami. Oni i tak wejdą URL-em, a Ty stracisz czas na debugowanie „znikającego” menu.
/**
* Oczyść menu admina dla nie-administratorów
*/
function wppoland_clean_admin_menu(): void {
if ( current_user_can( 'manage_options' ) ) {
return;
}
remove_menu_page( 'tools.php' );
remove_menu_page( 'options-general.php' );
remove_menu_page( 'edit-comments.php' );
remove_menu_page( 'edit.php?post_type=acf-field-group' );
remove_submenu_page( 'themes.php', 'theme-editor.php' );
remove_submenu_page( 'plugins.php', 'plugin-editor.php' );
}
add_action( 'admin_menu', 'wppoland_clean_admin_menu', 999 );Priorytet 999 jest świadomy. Wiele wtyczek rejestruje menu na admin_menu z domyślnym priorytetem 10. Jeśli odpalisz cleanup za wcześnie, slug jeszcze nie istnieje, remove_menu_page nic nie robi, a pozycje ACF albo WooCommerce zostają na miejscu. Gdy nie wiesz, jakiego sluga używa wtyczka, zaloguj tymczasowo $GLOBALS['menu'] na stagingu. ACF, Yoast i WooCommerce mają nieoczywiste ścieżki.
Ostrzeżenie warte powtórzenia: remove_menu_page tylko ukrywa link. Sprytny użytkownik nadal może wejść na /wp-admin/options-general.php bezpośrednio. Żeby zablokować dostęp, weryfikuj uprawnienia na admin_init albo current_screen i zwracaj wp_die(), gdy rola nie ma capability.
W praktyce warto też przemyśleć submenu WooCommerce. Shop manager często potrzebuje zamówień, produktów i kuponów, ale nie „WooCommerce > Status” ani rozszerzeń. Tu lepiej działa remove_submenu_page niż całkowite wycięcie całego drzewa.
Nie usuwaj „Mediów” redaktorom, którzy naprawdę wgrywają zdjęcia. To klasyczny nadmiarowy porządek: panel wygląda schludnie, a następnego dnia przychodzi ticket, że zniknął przycisk „Wyślij”. Najpierw narzędzia techniczne, ścieżki treści na końcu.
Dodawanie własnych stron menu
Nie używaj ciężkich frameworków opcji motywu, jeśli wystarczy jedna strona z kontaktem i krótką instrukcją. Natywne API jest lekkie i nie dokłada kolejnej warstwy aktualizacji.
function wppoland_register_support_page(): void {
add_menu_page(
'Wsparcie klienta',
'Wsparcie',
'edit_posts',
'wppoland-support',
'wppoland_render_support',
'dashicons-sos',
90
);
}
add_action( 'admin_menu', 'wppoland_register_support_page' );
function wppoland_render_support(): void {
if ( ! current_user_can( 'edit_posts' ) ) {
wp_die( esc_html__( 'Brak dostępu', 'wppoland-admin-ux' ) );
}
?>
<div class="wrap">
<h1>Potrzebujesz pomocy?</h1>
<div class="card">
<h2>Skontaktuj się z deweloperem</h2>
<p>Opisz problem i dołącz URL panelu oraz zrzut ekranu z konsoli przeglądarki.</p>
</div>
</div>
<?php
}Capability edit_posts otwiera stronę dla redaktorów. Jeśli strona ma zawierać sekrety wdrożeniowe (klucze staging, checklisty migracji), podnieś próg do manage_options albo zrób dwie strony: „Pomoc redakcji” i „Panel agencji”. Nie buduj w niej mini-CMS-a.
add_submenu_page przyda się, gdy chcesz podpiąć dokumentację pod istniejące „Ustawienia” albo pod własny top-level. Unikaj tworzenia pięciu top-leveli, bo to powtarza problem, który właśnie czyścisz.

Strona ustawień dla klienta w Settings API
Gdy klient ma sam zmieniać kilka wartości, na przykład telefon do wsparcia albo linki do social mediów, nie potrzebuje dostępu do Ustawień. Osobna strona zbudowana na Settings API pokazuje mu tylko te pola. Według podręcznika WordPress przejmuje wtedy zapis danych z formularza, dodaje nonce i wysyła formularz przez wp-admin/options.php, który ściśle sprawdza uprawnienia.
function wppoland_register_client_settings(): void {
register_setting(
'wppoland_client',
'wppoland_support_phone',
[
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
]
);
add_settings_section( 'wppoland_contact', 'Kontakt', '__return_false', 'wppoland-client-settings' );
add_settings_field(
'wppoland_support_phone',
'Telefon do wsparcia',
'wppoland_render_phone_field',
'wppoland-client-settings',
'wppoland_contact'
);
}
add_action( 'admin_init', 'wppoland_register_client_settings' );
function wppoland_render_phone_field(): void {
printf(
'<input type="text" name="wppoland_support_phone" value="%s" class="regular-text">',
esc_attr( (string) get_option( 'wppoland_support_phone', '' ) )
);
}
function wppoland_add_client_settings_page(): void {
add_menu_page(
'Ustawienia witryny',
'Witryna',
'edit_pages',
'wppoland-client-settings',
'wppoland_render_client_settings',
'dashicons-admin-generic',
91
);
}
add_action( 'admin_menu', 'wppoland_add_client_settings_page' );
function wppoland_render_client_settings(): void {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<?php settings_errors(); ?>
<form action="options.php" method="post">
<?php
settings_fields( 'wppoland_client' );
do_settings_sections( 'wppoland-client-settings' );
submit_button();
?>
</form>
</div>
<?php
}
add_filter( 'option_page_capability_wppoland_client', fn() => 'edit_pages' );Ostatniej linii zwykle brakuje. W pliku rdzenia wp-admin/options.php jest zapisane, że grupy opcji wszystkich zarejestrowanych ustawień domyślnie wymagają manage_options, a filtr option_page_capability_{$option_page} zmienia ten wymóg dla jednej grupy. Bez filtra redaktor widzi stronę, wypełnia pole i przy zapisie dostaje komunikat o braku uprawnień. Filtr otwiera tylko grupę wppoland_client, nie resztę Ustawień.
Ładowanie CSS i JS panelu przez admin_enqueue_scripts zamiast admin_head
admin_head z wstawionym <style> działa w demo, ale słabo się skaluje: brak wersjonowania, kłopotliwe odświeżanie cache i prezentacja wymieszana z PHP. Używaj admin_enqueue_scripts, ładuj własne pliki i ograniczaj je do ekranów, które ich potrzebują.
function wppoland_admin_assets( string $hook_suffix ): void {
// Tylko własne strony i kokpit, nie każdy ekran rdzenia.
$allowed = [
'index.php',
'toplevel_page_wppoland-support',
];
if ( ! in_array( $hook_suffix, $allowed, true ) ) {
return;
}
$base = plugin_dir_url( __FILE__ );
$ver = '2026.09.21';
wp_enqueue_style(
'wppoland-admin-ux',
$base . 'assets/admin.css',
[],
$ver
);
wp_enqueue_script(
'wppoland-admin-ux',
$base . 'assets/admin.js',
[ 'jquery' ],
$ver,
true
);
wp_localize_script(
'wppoland-admin-ux',
'wppolandAdminUx',
[
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wppoland_admin_ux' ),
]
);
}
add_action( 'admin_enqueue_scripts', 'wppoland_admin_assets' );$hook_suffix to klucz do tego, żeby nie psuć ekranów rdzenia. Globalny CSS chowający .notice na wszystkich stronach panelu usuwa też ostrzeżenia rdzenia o krytycznych aktualizacjach bezpieczeństwa. Zawężaj selektory do własnych wrapperów (.wppoland-support-wrap) albo własnych hook suffixów.
Na koniec szlif wizualny, tak żeby kolorystyka pokrywała się z brandingiem klienta albo Twojej agencji. Kolor admin bara możesz zmienić bez ryzyka, ale testuj klasyczny edytor i Gutenberga: edytor blokowy ma własny interfejs, a !important na .components-button to przepis na zepsuty edytor.
/* assets/admin.css: wąski zakres */
#wpadminbar {
background: #1f2937;
}
#toplevel_page_wppoland-support .wp-menu-image::before {
color: #059669;
}
.wppoland-support-wrap .card {
max-width: 40rem;
}Jeśli admin.js rozmawia z REST API, użyj wp_create_nonce( 'wp_rest' ) albo własnego nonce jak w przykładzie. „Tylko zalogowani” nie zastępuje sprawdzenia nonce na serwerze. Unikaj też wp_print_scripts bezpośrednio w callbacku add_menu_page, bo psuje kolejność zależności.
Własny schemat kolorów panelu WordPress przez wp_admin_css_color
Kolory menu bocznego i paska narzędzi pochodzą ze schematu kolorów, który każdy użytkownik wybiera w swoim profilu. Zamiast nadpisywać je przez !important, możesz zarejestrować własny schemat funkcją wp_admin_css_color. Przyjmuje ona unikalny klucz, nazwę, URL pliku CSS ze schematem, tablicę kolorów pokazywanych jako podgląd w profilu oraz opcjonalnie kolory ikon SVG (base, focus i current).
function wppoland_register_admin_scheme(): void {
wp_admin_css_color(
'wppoland',
'WPPoland',
plugin_dir_url( __FILE__ ) . 'assets/admin-colors.css',
[ '#1f2937', '#059669', '#d1fae5', '#ffffff' ],
[
'base' => '#d1d5db',
'focus' => '#ffffff',
'current' => '#ffffff',
]
);
}
add_action( 'admin_init', 'wppoland_register_admin_scheme' );Rdzeń rejestruje swoje schematy na tym samym hooku: register_admin_color_schemes jest podpięte pod admin_init z priorytetem 1 w pliku wp-admin/includes/admin-filters.php. Wybór zapisuje się jako opcja użytkownika admin_color, a panel dostaje klasę body admin-color- z kluczem schematu, więc dodatkowe reguły możesz kierować tylko do swojego schematu.
Filtrem get_user_option_admin_color wymusisz schemat dla wszystkich, ale wtedy odbierasz użytkownikom wybór. Niektórzy wybierają schemat, bo jego kontrast pasuje do ich wzroku. Własny schemat jako jedna z opcji to bezpieczniejsze ustawienie domyślne.
Jak ograniczyć powiadomienia w panelu WordPress
Drugie największe źródło ticketów po menu to notice’y. Wtyczki SEO, backupów i page builderów walczą o pasek nad listą wpisów. Klient czyta „aktualizuj do wersji premium” jako alarm.
Zamiast display: none na wszystkich .notice, podpinaj się pod admin_notices albo filtruj konkretne klasy lub teksty. Ukrywanie wszystkich notice’ów „na ślepo” jest ryzykowne: możesz schować krytyczne ostrzeżenia o wersji PHP albo o nieudanym cronie. Zostaw błędy PHP, ostrzeżenia o cronie i komunikaty własne. Na kontach z manage_options możesz pokazywać więcej, bo to Twój kanał diagnostyczny.
Dla ról klienta często wystarczy ukrycie notice’ów typu updated od wtyczek marketingowych, przy jednoczesnym zostawieniu error i notice-error. Warto też wyłączyć zachęty typu „wypróbuj Gutenberga” i podobne komunikaty na starych instalacjach, jeśli decyzja o edytorze już zapadła. Każdy zbędny banner to kolejne pytanie na spotkaniu z klientem.
Najczęstsze błędy przy customizacji panelu WordPress
Trzy błędy wracają w produkcji najczęściej:
- Globalne chowanie notice’ów. Reguła
.notice { display: none }usuwa zarówno spam wtyczek, jak i krytyczne ostrzeżenia rdzenia. Filtruj po klasie wtyczki albo zdejmuj znane callbacki zadmin_noticesprzezremove_action, zamiast chować wszystko. - Ukrycie Wtyczek albo Aktualizacji przed wszystkimi. Role klienta nie powinny instalować wtyczek, administrator musi. Jeśli schowasz Aktualizacje także przed
manage_options, tracisz widok na wydania bezpieczeństwa. - Przepisywanie ekranów Settings API. Nie zastępuj
options-general.phpwłasną “uproszczoną” stroną, jeśli nie odwzorujesz wszystkich wymaganych opcji z walidacją. Niekompletna kopia psuje bezpośrednie odnośniki, adres witryny i ustawienie języka.
Gdy dostosowujesz edytor blokowy, używaj enqueue_block_editor_assets osobno od admin_enqueue_scripts i ładuj tylko to, czego edytor potrzebuje. Nie wciągaj dużych paczek jQuery UI do post.php bez powodu.
Praktyczny test regresji: lista wpisów, Gutenberg dla strony, biblioteka mediów, WooCommerce > Zamówienia i Twoja strona wsparcia, jako administrator i jako redaktor. Jeśli któryś ekran gubi przyciski, notice’y albo nawigację klawiaturą, cofnij CSS przed oddaniem. White-label, który psuje interfejs rdzenia, nie jest skończoną pracą.
Dostosowywanie paska narzędzi (toolbar)
Toolbar jest widoczny na froncie dla zalogowanych użytkowników. To dobre miejsce na szybkie akcje: wyczyść cache, otwórz stronę w Page Builderze, przejdź do ostatnich zamówień.
function wppoland_customize_toolbar( WP_Admin_Bar $wp_admin_bar ): void {
$wp_admin_bar->remove_node( 'wp-logo' );
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$wp_admin_bar->add_node(
[
'id' => 'wppoland-clear-cache',
'title' => 'Wyczyść cache',
'href' => wp_nonce_url(
admin_url( 'admin-post.php?action=wppoland_clear_cache' ),
'wppoland_clear_cache'
),
'meta' => [ 'title' => 'Opróżnij object cache' ],
]
);
}
add_action( 'admin_bar_menu', 'wppoland_customize_toolbar', 999 );Usuwanie logo WordPressa to detal brandingowy. Ważniejsze jest to, żeby akcja „Wyczyść cache” miała nonce, capability check (check_admin_referer w handlerze admin_post_wppoland_clear_cache) i logowała, kto ją odpalił. Na sklepie z object cache Redis przypadkowe czyszczenie w szczycie ruchu potrafi podnieść TTFB, dopóki cache się nie nagrzeje.
Jeśli klient pracuje wyłącznie w wp-admin i nie potrzebuje bara na froncie, rozważ show_admin_bar filtrowane po roli. Redaktorzy często wolą czysty podgląd; deweloperzy zostawiają bar włączony.
Jak zastąpić domyślne widżety kokpitu WordPress
Kiedy klient się loguje, ląduje w kokpicie. Domyślne widżety (wydarzenia WordPress, szybki szkic) są dla niego zwykle bezużyteczne. Zastąp je statusem, który odpowiada na pytanie „czy coś się pali?”.
function wppoland_dashboard_widgets(): void {
remove_meta_box( 'dashboard_primary', 'dashboard', 'side' );
remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );
wp_add_dashboard_widget(
'wppoland_status_widget',
'Status strony',
'wppoland_render_status_widget'
);
}
add_action( 'wp_dashboard_setup', 'wppoland_dashboard_widgets' );
function wppoland_render_status_widget(): void {
echo '<p><strong>WordPress</strong>: ' . esc_html( get_bloginfo( 'version' ) ) . '</p>';
echo '<p><strong>Kopie zapasowe</strong>: sprawdź ostatnie zadanie we wtyczce backupu</p>';
echo '<p><a href="' . esc_url( admin_url( 'admin.php?page=wppoland-support' ) ) . '">Otwórz wsparcie</a></p>';
}Nie wpisuj na sztywno „aktualny” z zielonym ptaszkiem, jeśli widżet nie czyta prawdziwej wersji. Kłamiący widżet statusu jest gorszy niż żaden. W projektach z opieką techniczną warto pokazać datę ostatniego udanego backupu i wynik ostatniego health checka (PHP, cron, REST). Nie wklejaj tu marketingowych „newsów z bloga WordPress.org”, bo klient i tak ich nie czyta, a Ty tracisz miejsce nad foldem.
Callbacki widżetów mają być tanie. Nie wołaj zewnętrznych API synchronicznie przy renderowaniu kokpitu: wynik trzymaj w transiencie i pokazuj, kiedy został pobrany. Jeśli używasz kilku środowisk, dopisz badge „staging” albo „produkcja” na podstawie stałej w wp-config.php. To banalny detal, który zapobiega edycji treści na złym hoście.
Co naprawdę robią domyślne widżety kokpitu
Częstym argumentem za usunięciem domyślnych widżetów jest szybkość. Kod źródłowy wp-admin/includes/dashboard.php pokazuje, że zysk jest niewielki:
- Wydarzenia i aktualności WordPressa (
dashboard_primary) ładują kanał przez widżet z pamięcią podręczną. Gotowy HTML trafia do transientu osobno dla każdego języka, a gdy cache jest pusty, widżet pokazuje wskaźnik ładowania i dociąga treść przez AJAX, zamiast wstrzymywać stronę. - Szybki szkic (
dashboard_quick_press) używa ponownie jednego automatycznego szkicu na użytkownika, a jego ID trzyma w opcji użytkownikadashboard_quick_press_last_post_id. Automatyczne szkice starsze niż siedem dni usuwawp_delete_auto_drafts, uruchamiane przez zaplanowane zdarzeniewp_scheduled_auto_draft_delete. - Stan kondycji witryny (
dashboard_site_health) czyta zapisany wynik z transientuhealth-check-site-status-result, zamiast uruchamiać wszystkie testy przy każdym wejściu.
Argumentem za usunięciem jest więc skupienie, nie szybkość. Widżet kondycji witryny lepiej zostawić administratorom, bo pokazuje ostrzeżenia o wersji PHP i brakujących modułach, które inaczej łatwo przeoczyć.
Personalizacja ekranu logowania WordPress z logo i linkiem
Pierwszy punkt styku klienta z systemem to /wp-login.php. Zamiast logo WordPressa pokaż logo marki. Trzy hooki z wp-login.php załatwiają większość pracy:
login_enqueue_scripts(od wersji 3.1) to hook dla arkuszy stylów i skryptów na stronie logowania, odpowiednikadmin_enqueue_scriptsw panelu.login_headerurldecyduje, dokąd prowadzi logo. Domyślnie jest to wordpress.org.login_headertext(od wersji 5.2) ustawia tekst linku logo. Starszy filtrlogin_headertitlejest przestarzały od 5.2, z uzasadnieniem, że atrybut title na logo logowania nie jest zalecany ze względu na dostępność.

function wppoland_login_assets(): void {
wp_enqueue_style(
'wppoland-login',
plugin_dir_url( __FILE__ ) . 'assets/login.css',
[],
'2026.10.14'
);
}
add_action( 'login_enqueue_scripts', 'wppoland_login_assets' );
add_filter( 'login_headerurl', fn() => home_url( '/' ) );
add_filter( 'login_headertext', fn() => get_bloginfo( 'name' ) );/* assets/login.css */
.login h1 a {
background-image: url("../img/logo-klienta.svg");
background-size: contain;
width: 100%;
height: 80px;
}Dzięki login_headerurl kliknięcie logo prowadzi na front klienta, a nie na wordpress.org. Tekst linku czytają czytniki ekranu, więc nazwa witryny jest lepsza niż pusty ciąg. Na instalacjach z 2FA albo SSO upewnij się, że własny CSS nie psuje układu dodatkowych pól. Więcej technik dla samego formularza znajdziesz w poradniku o dostosowaniu strony logowania WordPress.
Jak rozdzielić role klienta i role agencji
W typowym projekcie masz co najmniej trzy poziomy dostępu: administrator agencji, redaktor klienta i ewentualnie shop manager albo autor. Każdy poziom dostaje inny zestaw menu. Redaktor publikuje treści i media. Shop manager ogarnia zamówienia. Administrator agencji widzi wtyczki, narzędzia okołoserwerowe i logi.
Nie buduj jednej „roli klienta” na siłę. Jeśli sklep i blog prowadzą różne osoby, rozdziel capability zamiast ukrywać te same pozycje CSS-em. Ukrywanie CSS-em to antywzorzec: pozycja znika wizualnie, a endpoint zostaje.
Przy WooCommerce sprawdź też role z wtyczek płatności i faktur. Często doklejają własne top-level menu z upsellami. Cleanup z priorytetem 999 powinien iść po ich rejestracji; inaczej wracają po każdej aktualizacji bramki.
Co sprawdzić przed oddaniem panelu klientowi
- Menu oczyszczone dla ról bez
manage_options, z późnym priorytetem; administrator nadal widzi wszystko. - Bezpośrednie URL-e do Ustawień i Narzędzi zablokowane capability checkiem.
- CSS i JS panelu ładowane tylko na dozwolonych
$hook_suffix. - Strona wsparcia z jasnym CTA i bez sekretów w treściach dla redaktorów.
- Strona ustawień zapisuje się z rolą klienta, nie tylko jako administrator.
- Toolbar bez martwych akcji; czyszczenie cache ma nonce i log.
- Kokpit pokazuje status backupów i środowiska, nie kanał WordPress.org.
- Branding logowania i poprawne linki nagłówka.
- Kod we wtyczce must-use, nie w motywie; przetestowany na roli edytora.
- Notice’y przefiltrowane per rola, nie globalnie wyzerowane.
- Role WooCommerce i wtyczek płatności przejrzane po aktualizacjach.
W README projektu zapisz, które menu są ukryte dla której roli i która wtyczka must-use jest właścicielem reguł. Kolejny deweloper (albo Ty za pół roku) nie powinien odtwarzać callbacków admin_menu, żeby zrozumieć, czemu shop manager nie widzi Ustawień.
Jak wdrożyć white-label panelu WordPress w 90 dni
Kolejność prac przy brandowaniu i zabezpieczaniu panelu:
- Dni 1-30 (kontrola dostępu): zarejestruj role klienta z minimalnymi uprawnieniami za kontrolą wersji. Dodaj hooki porządkujące menu we wtyczce must-use, żeby ukryć nieużywane opcje przed osobami bez roli administratora. Użyj
map_meta_cap, żeby role z ograniczeniami nie mogły edytować ani usuwać kluczowych stron (na przykład strony głównej albo kontaktu). - Dni 31-60 (kokpit i ustawienia): zbuduj widżety kokpitu, które pokazują prawdziwy status i linki do wsparcia. Przygotuj osobną stronę ustawień w Settings API z walidacją i otwórz jej grupę opcji dla roli klienta filtrem uprawnień. Dodaj skróty w pasku narzędzi zabezpieczone nonce.
- Dni 61-90 (branding i logowanie): ładuj style panelu przez
admin_enqueue_scripts, zarejestruj schemat kolorów i ostyluj ekran logowania. Na koniec przejdź checklistę oddania z każdą rolą klienta.
Co jest najważniejsze w customizacji panelu WordPress
Dostosowywanie panelu admina to UX produktu, nie kosmetyka. Usuwając bałagan i eksponując narzędzia, których klient naprawdę używa, zmniejszasz liczbę zgłoszeń i skracasz onboarding. Assety ładuj przez admin_enqueue_scripts, menu buduj i czyść na admin_menu, sprawdzaj capability przed ukryciem czy blokadą, a kod trzymaj we wtyczce must-use.
Jeśli przejmujesz stary WordPress z dziesiątkami ról i chaosem w menu, da się to uporządkować w ramach opieki developerskiej. Po audycie ról i wtyczek układam checklistę zmian w adminie i wdrażam je na stagingu przed produkcją.
Potrzebujesz takiego porządku na żywej instalacji? Napisz przez formularz kontaktowy. Wystarczy krótki opis ról użytkowników i lista wtyczek z panelu.







