Z czego składa się panel administracyjny WordPress
Panel administracyjny WordPress wygląda jak jedna aplikacja, ale technicznie to zestaw zwykłych plików PHP w katalogu wp-admin, spiętych jednym plikiem startowym i kilkudziesięcioma akcjami. Kto zna kolejność, w jakiej te akcje się wykonują, pisze wtyczki, które ładują skrypty tylko tam, gdzie trzeba, i nie psują ekranów innych wtyczek.
Ten tekst opisuje katalog wp-admin, drogę pojedynczego żądania przez panel, nazewnictwo ekranów ($hook_suffix, WP_Screen) i to, czym panel różni się od hierarchii szablonów motywu. Nazwy plików i kolejność akcji pochodzą z kodu WordPressa 7.1.3 (aktualne wydanie stabilne w chwili sprawdzenia) w lustrze repozytorium WordPress/WordPress na GitHubie.
Jaka jest struktura katalogu wp-admin w WordPress
Katalog wp-admin zawiera pliki zaplecza strony. W głównym katalogu leży po jednym pliku na ekran: edit.php to lista wpisów, post.php i post-new.php obsługują edycję, upload.php to biblioteka mediów, options-general.php, options-writing.php i pozostałe options-*.php to podstrony Ustawień, site-health.php to Zdrowie witryny. Adres w pasku przeglądarki zdradza więc, który plik właśnie się wykonuje. Obok leżą podkatalogi network/ (panel sieci w multisite), user/ (panel użytkownika w multisite) i maint/ (naprawa bazy danych).
Główne pliki katalogu wp-admin
admin.php - Plik startowy panelu. Każdy ekran zaczyna się od require_once __DIR__ . '/admin.php';. To tutaj:
- Definiowana jest stała
WP_ADMINi ładowanywp-load.php - Dołączane są funkcje z
wp-admin/includes/admin.php - Funkcja
auth_redirect()odsyła niezalogowanych do ekranu logowania - Budowane jest menu i ustalany bieżący ekran
index.php - Strona główna panelu (Dashboard). Wyświetla:
- Widgety pulpitu
- Szybkie akcje
- Aktualności WordPress
menu.php - Definicja struktury menu panelu:
- Rejestracja pozycji menu
- Uprawnienia dostępu do poszczególnych sekcji
- Ikony i etykiety menu
Podkatalogi wp-admin
includes/ - Zawiera wszystkie pliki biblioteczne:
class-wp-screen.php- Klasa zarządzająca ekranami panelutemplate.php- Funkcje pomocnicze do szablonówmedia.php- Funkcjonalność obsługi mediówajax-actions.php- Handlery AJAX
css/ - Arkusze stylów panelu:
wp-admin.css- Arkusz zbiorczy, który importuje pozostałecommon.css- Style wspólne (kontener.wrap, powiadomienia.notice)colors/- Schematy kolorówforms.css- Style formularzy
Każdy arkusz występuje w czterech wariantach: źródłowym, zminifikowanym (.min.css) i w dwóch wersjach dla języków pisanych od prawej do lewej (-rtl.css, -rtl.min.css). To samo dotyczy skryptów w js/: obok common.js leży common.min.js. Na produkcji WordPress podaje wersje .min, a stała SCRIPT_DEBUG przełącza go na źródłowe, o czym niżej.
js/ - Skrypty JavaScript:
common.js- Wspólne funkcjonalnościpost.js- Obsługa edycji wpisówmedia-upload.js- Upload mediów
Co robi WordPress po wejściu na adres wp-admin
Weźmy żądanie wp-admin/admin.php?page=moja-strona, czyli ekran dodany przez wtyczkę. Plik admin.php w wersji 7.1.3 przechodzi kolejno przez takie kroki:
- Definiuje
WP_ADMINi dołączawp-load.php, a więcwp-config.php, wtyczki i motyw. Od tej chwiliis_admin()zwracatrue. - Dołącza
wp-admin/includes/admin.php, czyli całe API panelu, i wywołujeauth_redirect(). - Dołącza
wp-admin/menu.php. Ten plik buduje tablice$menui$submenuz pozycjami rdzenia, a na końcu dołączawp-admin/includes/menu.php, który odpala akcjęadmin_menu. Właśnie w niej wtyczki wołająadd_menu_page(). - Odpala akcję
admin_init. - Na podstawie parametru
pagewylicza nazwę haka strony (get_plugin_page_hook()) i zapisuje ją w globalnej zmiennej$hook_suffix. - Wywołuje
set_current_screen(), która tworzy obiektWP_Screendla tego ekranu. - Odpala akcję
load-{$page_hook}, jeszcze przed wysłaniem jakiegokolwiek HTML. - Dołącza
admin-header.php, chyba że w adresie jest parametrnoheader. - Odpala akcję o nazwie
$page_hook. Pod niąadd_menu_page()podpięła wcześniej callback wtyczki. - Dołącza
admin-footer.phpi kończy działanie przezexit.
Z tej kolejności wynikają dwie rzeczy, które w praktyce psują wtyczki. Po pierwsze admin_menu wykonuje się przed admin_init, a nie po nim, więc kod rejestrujący menu nie może zakładać, że coś ustawione w admin_init już istnieje. Po drugie get_current_screen() zwraca null aż do kroku 6, bo funkcja oddaje globalny obiekt tylko wtedy, gdy jest on instancją WP_Screen. Wołanie jej w admin_init daje więc null, a w load-{$page_hook} już działający obiekt.
Akcja load-{$page_hook} to najlepsze miejsce na obsługę formularza, który po zapisie robi przekierowanie. Nagłówki HTTP nie zostały jeszcze wysłane, więc wp_safe_redirect() zadziała, a kod wykona się tylko na tym jednym ekranie. Komentarz w admin.php podaje przykład nazwy: dla strony wtyczki dodanej pod Ustawieniami hak nazywa się load-settings_page_pluginbasename.
Co wykonuje się w admin-header.php i admin-footer.php
Nagłówek panelu też jest sekwencją akcji, a ich kolejność decyduje o tym, gdzie podpiąć style i skrypty. W admin-header.php WordPress odpala kolejno:
admin_enqueue_scriptsz argumentem$hook_suffix, czyli miejsce nawp_enqueue_script()iwp_enqueue_style(),admin_print_styles-{$hook_suffix}iadmin_print_styles,admin_print_scripts-{$hook_suffix}iadmin_print_scripts,admin_head-{$hook_suffix}iadmin_head,- potem dołącza
menu-header.php, który rysuje boczne menu, i odpalain_admin_header, - na końcu
admin_notices(albonetwork_admin_noticesiuser_admin_noticesw multisite) orazall_admin_notices.
W admin-footer.php kolejność jest podobna: in_admin_footer, admin_footer, admin_print_footer_scripts-{$hook_suffix}, admin_print_footer_scripts i admin_footer-{$hook_suffix}. Wersje z sufiksem wykonują się tylko na jednym ekranie, więc zastępują warunek if w callbacku. Dokumentacja haka admin_enqueue_scripts nazywa ją właściwym hakiem do dołączania skryptów i stylów panelu (mimo nazwy obsługuje oba typy) i zaleca, by na podstawie $hook_suffix ładować je tylko na ekranach, które ich używają.
Ekrany rdzenia, takie jak options-general.php, robią to samo bez parametru page: zaczynają od require_once __DIR__ . '/admin.php';, przygotowują dane, potem same dołączają admin-header.php, drukują formularz i dołączają admin-footer.php.
Jak działa system szablonów panelu administracyjnego
WordPress nie używa tradycyjnego systemu szablonów w panelu administracyjnym takiego jak na froncie. Zamiast tego wykorzystuje strukturę opartą na funkcjach callback i hookach.
Struktura strony panelu
Każda strona panelu składa się z trzech głównych części. Poniższy szkic pokazuje układ ekranu rdzenia, który sam dołącza nagłówek i stopkę. Callback strony wtyczki tego nie robi, bo nagłówek i stopkę dołącza za niego admin.php (kroki 8 i 10 powyżej). admin.php dołącza nagłówek przez require_once, więc drugie require_once w callbacku nic nie zrobi, ale zwykłe require albo include wydrukuje nagłówek panelu drugi raz.
// Przykładowa struktura ekranu rdzenia, np. wp-admin/options-general.php
require_once __DIR__ . '/admin.php';
// 1. Nagłówek (header)
require_once ABSPATH . 'wp-admin/admin-header.php';
// 2. Zawartość strony (content)
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<!-- Tutaj znajduje się właściwa zawartość -->
</div>
<?php
// 3. Stopka (footer)
require_once ABSPATH . 'wp-admin/admin-footer.php';Funkcje callback stron panelu
Każda strona panelu ma przypisaną funkcję callback, która renderuje jej zawartość:
// Rejestracja strony panelu
add_action('admin_menu', function() {
add_menu_page(
'Moja Strona', // Tytuł strony
'Moja Strona', // Etykieta menu
'manage_options', // Uprawnienia
'moja-strona', // Slug
'moja_strona_callback', // Funkcja callback
'dashicons-admin-generic',
30
);
});
// Funkcja callback renderująca zawartość
function moja_strona_callback() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields('moja_grupa_opcji');
do_settings_sections('moja-strona');
submit_button('Zapisz zmiany');
?>
</form>
</div>
<?php
}Jak WordPress nazywa hook_suffix stron wtyczek
Wartość zwracana przez add_menu_page() i add_submenu_page() to $hook_suffix nowego ekranu. Ten sam ciąg trafia do admin_enqueue_scripts, do haków load-* i do WP_Screen, więc warto wiedzieć, skąd się bierze. Liczy go funkcja get_plugin_page_hookname() w wp-admin/includes/plugin.php według schematu {typ}_page_{slug}:
- strona najwyższego poziomu dodana przez
add_menu_page()dostaje typtoplevel, czylitoplevel_page_moja-strona, - podstrona pod menu rdzenia dostaje typ wyliczony z nazwy pliku rodzica, na przykład
settings_page_moja-stronapod Ustawieniami, - podstrona pod menu innej wtyczki dostaje typ z tablicy
$admin_page_hooks, w którejadd_menu_page()zapisujesanitize_title( $menu_title ).
Ostatni punkt to pułapka dla polskich instalacji. Jeśli tytuł menu nadrzędnego przechodzi przez __(), to po zmianie języka strony zmienia się też sufiks wszystkich jego podstron. Menu o tytule __( 'Reports' ) daje podstronie sufiks reports_page_moje-raporty, ale po przełączeniu panelu na polski tytuł brzmi “Raporty” i sufiks zmienia się na raporty_page_moje-raporty. Kod, który porównuje $hook z pierwszą wartością wpisaną na stałe, przestaje wtedy działać. Bezpieczniej zapisać wartość zwróconą przez add_submenu_page() we właściwości klasy i porównywać z nią, albo sprawdzać get_current_screen()->id.
Do czego służą WP_Screen i get_current_screen
Obiekt WP_Screen opisuje bieżący ekran panelu. Jego publiczne właściwości to między innymi id, base, parent_base, post_type, taxonomy i action. Dla listy stron id to edit-page, base to edit, a post_type to page, więc jeden warunek na post_type obejmuje listę, edycję i dodawanie nowego elementu danego typu.
Na tym obiekcie wiszą też dwa elementy interfejsu, które często są pisane od zera bez potrzeby: zakładki pomocy (add_help_tab()) i opcje ekranu (add_option(), na przykład liczba elementów na stronie listy). Oba należy dodawać w akcji load-{$page_hook}, bo dopiero wtedy obiekt ekranu istnieje, a nagłówek z przyciskami “Pomoc” i “Opcje ekranu” jeszcze się nie wydrukował.
Jakie klasy body dostaje ekran panelu
admin-header.php buduje atrybut class znacznika body z kilku źródeł. Pierwsza klasa to $hook_suffix po zamianie znaków spoza a-z0-9_- na myślnik, więc ekran wtyczki ma w body klasę toplevel_page_moja-strona. Dalej dochodzą między innymi post-type-{typ} i taxonomy-{taksonomia} z obiektu ekranu, admin-color-{schemat} z ustawień profilu użytkownika, locale-{język}, branch-{wersja} i version-{wersja}, multisite i network-admin w sieci oraz wp-theme-{motyw} z nazwą aktywnego motywu. Własne klasy dodaje się filtrem admin_body_class. Dzięki temu CSS wtyczki może celować w jeden ekran przez selektor klasy, bez ładowania osobnego arkusza.
Jak działają hooki w panelu administracyjnym WordPress
Hooki to mechanizm, który pozwala modyfikować zachowanie panelu bez ingerencji w pliki core WordPressa.
Akcje w panelu administracyjnym
Akcje pozwalają wykonywać kod w określonym momencie:
// Dodanie skryptu tylko w panelu administracyjnym
add_action('admin_enqueue_scripts', function($hook) {
// $hook zawiera identyfikator aktualnej strony
if ($hook === 'toplevel_page_moja-strona') {
wp_enqueue_script(
'moja-strona-js',
plugin_dir_url(__FILE__) . 'js/admin.js',
['jquery'],
'1.0.0',
true
);
}
});
// Modyfikacja nagłówka admina
add_action('admin_head', function() {
?>
<style>
#wpadminbar { background: #1d2327; }
</style>
<?php
});
// Dodanie powiadomienia
add_action('admin_notices', function() {
?>
<div class="notice notice-success is-dismissible">
<p>Twoje zmiany zostały zapisane!</p>
</div>
<?php
});Filtry w panelu administracyjnym
Filtry pozwalają modyfikować dane przed ich wyświetleniem lub zapisaniem:
// Modyfikacja tytułu strony panelu
add_filter('admin_title', function($admin_title, $title) {
return $title . ' | Moja Strona';
}, 10, 2);
// Zmiana tekstu na footerze panelu
add_filter('admin_footer_text', function($text) {
return 'Dziękujemy za tworzenie z <a href="https://wordpress.org">WordPress</a> | Wsparcie techniczne: Moja Firma';
});
// Modyfikacja kolumn w liście wpisów
add_filter('manage_posts_columns', function($columns) {
$new_columns = [];
foreach ($columns as $key => $value) {
$new_columns[$key] = $value;
if ($key === 'title') {
$new_columns['moja_kolumna'] = 'Moja Dodatkowa Kolumna';
}
}
return $new_columns;
});Jakie klasy CSS używa panel administracyjny WordPress
WordPress posiada rozbudowany system klas CSS, który zapewnia spójny wygląd panelu.
Klasy CSS kontenerów panelu
/* Główny kontener strony */
.wrap {
margin: 10px 20px 0 2px;
}
/* Nagłówek strony */
.wrap h1 {
font-size: 23px;
font-weight: 400;
margin: 0;
padding: 9px 0 4px;
line-height: 1.3;
}
/* Metaboxy */
.postbox {
min-width: 255px;
border: 1px solid #c3c4c7;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
background: #fff;
}
/* Formularze */
.form-table {
border-collapse: collapse;
margin-top: 0.5em;
width: 100%;
clear: both;
}
.form-table th {
vertical-align: top;
text-align: left;
padding: 20px 10px 20px 0;
width: 200px;
line-height: 1.3;
font-weight: 600;
}Klasy CSS przycisków i komponentów UI
/* Przyciski (wp-includes/css/buttons.css) */
.wp-core-ui .button-primary {
background: var(--wp-admin-theme-color, #3858e9);
border-color: var(--wp-admin-theme-color, #3858e9);
color: #fff;
text-decoration: none;
text-shadow: none;
}
/* Powiadomienia (wp-admin/css/common.css) */
.notice {
background: #fff;
border: none;
border-left: 4px solid #c3c4c7;
box-shadow: none;
margin: 5px 15px 2px;
padding: 8px 12px;
}
.notice-success {
border-left-color: #4ab866;
}
.notice-error {
border-left-color: #cc1818;
}Wartości powyżej pochodzą z plików buttons.css i common.css w wersji 7.1.3. Kolor głównego przycisku nie jest wpisany na sztywno: idzie ze zmiennej CSS --wp-admin-theme-color, którą ustawia wybrany schemat kolorów, a #3858e9 to tylko wartość zapasowa. Wtyczka, która w swoim arkuszu wpisze kolor przycisku ręcznie, rozjedzie się z panelem użytkownika, który wybrał inny schemat w profilu. Bezpieczniej użyć klas rdzenia (button button-primary, notice notice-success) i nie nadpisywać ich kolorów.
Jak tworzyć własne strony w panelu administracyjnym
Poniżej najkrótszy działający przykład strony opcji na Settings API. Pełny przewodnik po porządkowaniu menu, rolach klienta, schematach kolorów i widżetach kokpitu jest w osobnym tekście o customizacji panelu administratora WordPress.
Jak dodać prostą stronę opcji
class Moja_Strona_Opcji {
public function __construct() {
add_action('admin_menu', [$this, 'dodaj_strone']);
add_action('admin_init', [$this, 'zarejestruj_ustawienia']);
}
public function dodaj_strone() {
add_options_page(
'Ustawienia Moje',
'Moje Ustawienia',
'manage_options',
'moje-ustawienia',
[$this, 'wyswietl_strone']
);
}
public function zarejestruj_ustawienia() {
register_setting('moje_ustawienia_grupa', 'moja_opcja_1');
register_setting('moje_ustawienia_grupa', 'moja_opcja_2');
add_settings_section(
'moja_sekcja',
'Główne Ustawienia',
[$this, 'wyswietl_sekcje'],
'moje-ustawienia'
);
add_settings_field(
'moja_opcja_1',
'Opcja Pierwsza',
[$this, 'wyswietl_pole_1'],
'moje-ustawienia',
'moja_sekcja'
);
}
public function wyswietl_strone() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('moje_ustawienia_grupa');
do_settings_sections('moje-ustawienia');
submit_button('Zapisz ustawienia');
?>
</form>
</div>
<?php
}
public function wyswietl_pole_1() {
$wartosc = get_option('moja_opcja_1');
?>
<input type="text" name="moja_opcja_1" value="<?php echo esc_attr($wartosc); ?>" class="regular-text">
<?php
}
}
new Moja_Strona_Opcji();Czym różni się admin-ajax.php od admin-post.php
Panel ma dwa osobne wejścia dla żądań, które nie renderują ekranu, i oba omijają admin.php.
admin-ajax.php obsługuje żądania AJAX. Definiuje stałą DOING_AJAX, ładuje wp-load.php i API panelu, wysyła nagłówek X-Robots-Tag: noindex, odpala admin_init, a potem akcję wp_ajax_{action} dla zalogowanych albo wp_ajax_nopriv_{action} dla gości. Brak parametru action kończy się odpowiedzią 0 z kodem 400, a brak zarejestrowanego handlera również odpowiedzią 0 z kodem 400. Na ekranach panelu adres tego pliku jest od razu dostępny w zmiennej JavaScript ajaxurl, którą drukuje admin-header.php. Handbook opisuje cały przepływ w rozdziale o AJAX we wtyczkach.
admin-post.php służy do formularzy wysyłanych zwykłym POST-em, po których ma nastąpić przekierowanie. Odpala admin_init, a potem admin_post_{action} dla zalogowanych albo admin_post_nopriv_{action} dla gości. Jeśli pod daną akcją nic nie jest podpięte, plik kończy się pustą odpowiedzią z kodem 400. Opis haka jest na stronie admin_post_{$action}.
Wspólny element obu plików ma konsekwencje dla wydajności. admin_init wykonuje się przy każdym żądaniu do admin-ajax.php, także przy cyklicznych żądaniach Heartbeat API (akcja heartbeat jest na liście akcji rdzenia, a dla gości działa wp_ajax_nopriv_heartbeat). Ciężki kod podpięty pod admin_init bez warunku wp_doing_ajax() wykona się więc nie tylko przy otwarciu ekranu, ale też przy każdym takim tyknięciu.
Czym panel różni się od hierarchii szablonów motywu
Słowo “szablon” w tytule tego tekstu łatwo pomylić z szablonami motywu. Front strony wybiera plik według hierarchii szablonów, którą Theme Handbook opisuje tak:
“Gdy odwiedzający trafia na dowolną stronę witryny WordPress, WordPress na podstawie query stringa decyduje, którego szablonu użyć do jej wyświetlenia. Query string zawiera dane z adresu URL, które pomagają w tej decyzji.”
Theme Handbook, Template hierarchy, tłumaczenie własne
W motywach blokowych ta sama strona dokumentacji wymienia trzy miejsca, w których WordPress szuka szablonu, w kolejności od najważniejszego: szablony utworzone przez użytkownika i zapisane w bazie jako typ wpisu wp_template, katalog /templates motywu potomnego i katalog /templates motywu nadrzędnego.
Panel nie ma żadnego z tych mechanizmów. Nie ma pliku zapasowego w rodzaju index.php motywu, nie ma dopasowania po typie zapytania. O tym, co się wyświetli, decyduje nazwa pliku w wp-admin albo parametr page, a układ ekranu to zawsze ten sam nagłówek, menu i stopka z rdzenia. Motyw nie ma wpływu na wygląd panelu, poza tym, że jego nazwa trafia do klasy wp-theme-* w body. Dlatego style i skrypty panelu dołącza się przez admin_enqueue_scripts, a frontu przez wp_enqueue_scripts. Pomylenie tych dwóch akcji kończy się arkuszem panelu na froncie albo odwrotnie.
Jak zabezpieczyć panel administracyjny WordPress
Sprawdzanie uprawnień użytkownika
Zawsze weryfikuj uprawnienia użytkownika:
// Sprawdź czy użytkownik ma odpowiednie uprawnienia
if (!current_user_can('manage_options')) {
wp_die(__('Nie masz uprawnień do tej strony.', 'text-domain'));
}
// Sprawdź nonce dla akcji
if (!isset($_POST['moj_nonce']) || !wp_verify_nonce($_POST['moj_nonce'], 'moja_akcja')) {
wp_die(__('Nieprawidłowy token zabezpieczający.', 'text-domain'));
}Sanityzacja danych z formularzy panelu
// Sanityzacja tekstu
$tytuł = sanitize_text_field($_POST['tytuł']);
// Sanityzacja HTML
$content = wp_kses_post($_POST['content']);
// Sanityzacja URL
$url = esc_url_raw($_POST['url']);
// Sanityzacja email
$email = sanitize_email($_POST['email']);Jak debugować panel administracyjny WordPress
Jak włączyć WP_DEBUG w panelu
// W wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', true); // Używa niezminifikowanych skryptówDokumentacja Debugging in WordPress opisuje SCRIPT_DEBUG jako stałą, która każe WordPressowi używać wersji deweloperskich plików CSS i JavaScript rdzenia zamiast zminifikowanych, i wskazuje ją jako przydatną przy testowaniu zmian we wbudowanych plikach .js i .css. Domyślnie ma wartość false. W praktyce panelu oznacza to, że zamiast wp-admin/js/common.min.js przeglądarka dostaje common.js, a stack trace z konsoli wskazuje czytelną linię zamiast jednej długiej linii pliku .min. Ta sama strona przypomina, że definicje trzeba wpisać przed komentarzem kończącym edycję w wp-config.php.
Logowanie zdarzeń panelu do debug.log
function log_admin_action($action, $data) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("Admin Action [{$action}]: " . print_r($data, true));
}
}
// Użycie
log_admin_action('user_login', ['user_id' => get_current_user_id()]);Co zapamiętać o panelu administracyjnym WordPress
Zrozumienie anatomii panelu administracyjnego WordPress otwiera przed developerami ogromne możliwości:
- Struktura katalogów - Wiedza o organizacji plików pozwala na efektywne debugowanie i modyfikacje
- System szablonów - Funkcje callback i hooki dają pełną kontrolę nad renderowaniem
- Bezpieczeństwo - Weryfikacja uprawnień i sanityzacja danych to podstawa
- Klasy CSS - Spójność z core WordPressa zapewnia profesjonalny wygląd
- Debugowanie - Odpowiednie narzędzia przyspieszają rozwój
Panel administracyjny WordPress to potężne narzędzie, które można dostosować do niemal każdych potrzeb. Kluczem do sukcesu jest zrozumienie jego architektury i wykorzystanie wbudowanych mechanizmów zamiast prób obchodzenia ich.
Pamiętaj: zawsze stosuj się do standardów WordPress Coding Standards i testuj swoje modyfikacje w środowisku staging przed wdrożeniem na produkcję.
Modyfikacje panelu administracyjnego przeżywają aktualizacje tylko wtedy, gdy korzystają z wbudowanych mechanizmów zamiast je obchodzić. Takie zmiany przygotowujemy i sprawdzamy na stagingu w ramach customizacji WordPressa.







