Hva WordPress-administrasjonspanelet består av
Administrasjonspanelet i WordPress ser ut som én applikasjon, men teknisk er det en samling vanlige PHP-filer i katalogen wp-admin, holdt sammen av én oppstartsfil og noen titalls actions. Utviklere som kjenner rekkefølgen disse actions kjøres i, skriver utvidelser som laster skript bare der de trengs og ikke ødelegger skjermene til andre utvidelser.
Denne artikkelen beskriver katalogen wp-admin, veien én enkelt forespørsel tar gjennom panelet, hvordan skjermene navngis ($hook_suffix, WP_Screen) og hvordan panelet skiller seg fra malhierarkiet i temaet. Filnavn og rekkefølgen på actions er hentet fra koden i WordPress 7.1.3 (gjeldende stabile versjon da dette ble sjekket) i speilet WordPress/WordPress på GitHub.
Hvordan katalogstrukturen i wp-admin ser ut i WordPress
Katalogen wp-admin inneholder filene for nettstedets bakside. På øverste nivå ligger én fil per skjerm: edit.php er innleggslisten, post.php og post-new.php håndterer redigering, upload.php er Mediebibliotek, options-general.php, options-writing.php og de øvrige options-*.php er undersidene under Innstillinger, og site-health.php er Nettstedshelse. Adressen i nettleserfeltet forteller altså hvilken fil som kjører. Ved siden av ligger underkatalogene network/ (nettverksadministrasjon i multisite), user/ (brukerpanelet i multisite) og maint/ (databasereparasjon).
Hovedfiler i katalogen wp-admin
admin.php - Oppstartsfilen for panelet. Hver skjerm begynner med require_once __DIR__ . '/admin.php';. Her:
- defineres konstanten
WP_ADMINogwp-load.phplastes - inkluderes funksjonene fra
wp-admin/includes/admin.php - sender
auth_redirect()utloggede besøkende til innloggingen - bygges menyen og gjeldende skjerm fastsettes
index.php - Kontrollpanel, startsiden i panelet. Den viser:
- widgeter på kontrollpanelet
- hurtighandlinger
- nyheter fra WordPress
menu.php - Definisjonen av menystrukturen i panelet:
- registrering av menypunkter
- tilgangsrettighetene for de enkelte seksjonene
- ikoner og etiketter i menyen
Underkataloger i wp-admin
includes/ - Inneholder alle bibliotekfilene:
class-wp-screen.php- klassen som styrer skjermene i panelettemplate.php- hjelpefunksjoner for malermedia.php- mediehåndteringajax-actions.php- AJAX-behandlere
css/ - Stilarkene i panelet:
wp-admin.css- samlestilarket som importerer de andrecommon.css- felles stiler (beholderen.wrap, varslene.notice)colors/- fargeskjemaerforms.css- stiler for skjemaer
Hvert stilark finnes i fire varianter: kildefilen, en minifisert versjon (.min.css) og to versjoner for språk som skrives fra høyre mot venstre (-rtl.css, -rtl.min.css). Det samme gjelder skriptene i js/: ved siden av common.js ligger common.min.js. I produksjon leverer WordPress .min-versjonene, og konstanten SCRIPT_DEBUG bytter til kildefilene, mer om det nedenfor.
js/ - JavaScript-filer:
common.js- felles funksjonalitetpost.js- skjermen for redigering av innleggmedia-upload.js- opplasting av medier
Hva WordPress gjør når du åpner en wp-admin-adresse
Ta forespørselen wp-admin/admin.php?page=min-side, altså en skjerm lagt til av en utvidelse. Filen admin.php i versjon 7.1.3 går gjennom disse stegene i rekkefølge:
- Den definerer
WP_ADMINog inkludererwp-load.php, og dermedwp-config.php, utvidelsene og temaet. Fra dette øyeblikket returnereris_admin()verdientrue. - Den inkluderer
wp-admin/includes/admin.php, altså hele panelets API, og kallerauth_redirect(). - Den inkluderer
wp-admin/menu.php. Denne filen bygger tabellene$menuog$submenumed kjernens menypunkter og inkluderer til sluttwp-admin/includes/menu.php, som utløser actionadmin_menu. Det er her utvidelser kalleradd_menu_page(). - Den utløser action
admin_init. - Ut fra parameteren
pageregner den ut navnet på sidekroken (get_plugin_page_hook()) og lagrer det i den globale variabelen$hook_suffix. - Den kaller
set_current_screen(), som oppretter objektetWP_Screenfor denne skjermen. - Den utløser action
load-{$page_hook}, før noe HTML er sendt. - Den inkluderer
admin-header.php, med mindre adressen inneholder parameterennoheader. - Den utløser action med navnet
$page_hook. Der haradd_menu_page()tidligere hektet på utvidelsens callback. - Den inkluderer
admin-footer.phpog avslutter medexit.
To følger av denne rekkefølgen ødelegger utvidelser i praksis. For det første kjøres admin_menu før admin_init, ikke etter, så kode som registrerer menyen kan ikke anta at noe som settes opp i admin_init allerede finnes. For det andre returnerer get_current_screen() verdien null frem til steg 6, fordi funksjonen bare returnerer det globale objektet når det er en instans av WP_Screen. Et kall i admin_init gir derfor null, mens det i load-{$page_hook} allerede gir et fungerende objekt.
Action load-{$page_hook} er det beste stedet å behandle et skjema som omdirigerer etter lagring. HTTP-hodene er ikke sendt ennå, så wp_safe_redirect() fungerer, og koden kjører bare på denne ene skjermen. En kommentar i admin.php gir et eksempel på navnet: for en utvidelsesside lagt under Innstillinger heter kroken load-settings_page_pluginbasename.
Hva som kjøres i admin-header.php og admin-footer.php
Toppen av panelet er også en sekvens av actions, og rekkefølgen deres avgjør hvor stiler og skript bør hektes på. I admin-header.php utløser WordPress i rekkefølge:
admin_enqueue_scriptsmed argumentet$hook_suffix, stedet forwp_enqueue_script()ogwp_enqueue_style(),admin_print_styles-{$hook_suffix}ogadmin_print_styles,admin_print_scripts-{$hook_suffix}ogadmin_print_scripts,admin_head-{$hook_suffix}ogadmin_head,- deretter inkluderes
menu-header.php, som tegner sidemenyen, ogin_admin_headerutløses, - til slutt
admin_notices(ellernetwork_admin_noticesoguser_admin_noticesi multisite) ogall_admin_notices.
I admin-footer.php er rekkefølgen lik: in_admin_footer, admin_footer, admin_print_footer_scripts-{$hook_suffix}, admin_print_footer_scripts og admin_footer-{$hook_suffix}. Versjonene med suffiks kjøres bare på én skjerm, så de erstatter en if-betingelse i callbacken. Dokumentasjonen for kroken admin_enqueue_scripts kaller den riktig krok for å legge til skript og stiler i administrasjonen (navnet til tross håndterer den begge typer) og anbefaler å bruke $hook_suffix til å laste dem bare på skjermene som bruker dem.
Kjerneskjermer som options-general.php gjør det samme uten parameteren page: de begynner med require_once __DIR__ . '/admin.php';, forbereder dataene, inkluderer selv admin-header.php, skriver ut skjemaet og inkluderer admin-footer.php.
Hvordan malsystemet i administrasjonspanelet fungerer
WordPress bruker ikke et tradisjonelt malsystem i administrasjonspanelet slik det gjør på forsiden. I stedet bygger det visningene av callback-funksjoner og kroker.
Strukturen på en skjerm i panelet
Hver skjerm i panelet består av tre hoveddeler. Skissen nedenfor viser oppbyggingen av en kjerneskjerm, som selv inkluderer topp og bunn. Callbacken til en utvidelsesside gjør ikke det, fordi admin.php inkluderer topp og bunn for den (steg 8 og 10 ovenfor). admin.php inkluderer toppen med require_once, så en ny require_once i callbacken gjør ingenting, men en vanlig require eller include skriver ut toppen av panelet en gang til.
// Eksempel på strukturen til en kjerneskjerm, f.eks. wp-admin/options-general.php
require_once __DIR__ . '/admin.php';
// 1. Topp (header)
require_once ABSPATH . 'wp-admin/admin-header.php';
// 2. Sideinnhold (content)
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<!-- Her kommer selve innholdet -->
</div>
<?php
// 3. Bunn (footer)
require_once ABSPATH . 'wp-admin/admin-footer.php';Callback-funksjoner for sider i panelet
Hver side i panelet har en tilknyttet callback-funksjon som skriver ut innholdet:
// Registrer en side i panelet
add_action('admin_menu', function() {
add_menu_page(
'Min side', // Sidetittel
'Min side', // Menyetikett
'manage_options', // Rettighet
'min-side', // Slug
'min_side_callback', // Callback-funksjon
'dashicons-admin-generic',
30
);
});
// Callback-funksjon som skriver ut innholdet
function min_side_callback() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields('min_innstillingsgruppe');
do_settings_sections('min-side');
submit_button('Lagre endringene');
?>
</form>
</div>
<?php
}Hvordan WordPress navngir hook_suffix for utvidelsessider
Verdien som add_menu_page() og add_submenu_page() returnerer, er $hook_suffix for den nye skjermen. Den samme strengen går til admin_enqueue_scripts, til load-*-krokene og til WP_Screen, så det lønner seg å vite hvor den kommer fra. Den regnes ut av funksjonen get_plugin_page_hookname() i wp-admin/includes/plugin.php etter mønsteret {type}_page_{slug}:
- en side på øverste nivå lagt til med
add_menu_page()får typentoplevel, altsåtoplevel_page_min-side, - en underside under en kjernemeny får en type utledet av filnavnet til foreldremenyen, for eksempel
settings_page_min-sideunder Innstillinger, - en underside under menyen til en annen utvidelse får typen fra tabellen
$admin_page_hooks, deradd_menu_page()lagrersanitize_title( $menu_title ).
Det siste punktet er en felle på oversatte nettsteder. Hvis tittelen på foreldremenyen går gjennom __(), endres suffikset til alle undersidene når språket på nettstedet endres. En meny med tittelen __( 'Reports' ) gir undersiden suffikset reports_page_mine-rapporter, men når utvidelsens oversettelse gjør tittelen til “Rapporter” i et norsk panel, blir suffikset rapporter_page_mine-rapporter. Kode som sammenligner $hook med den første verdien som ble skrevet inn fast, slutter da å virke. Det er tryggere å lagre verdien som add_submenu_page() returnerer, i en klasseegenskap og sammenligne med den, eller å sjekke get_current_screen()->id.
Hva WP_Screen og get_current_screen brukes til
Objektet WP_Screen beskriver gjeldende skjerm i panelet. De offentlige egenskapene omfatter blant annet id, base, parent_base, post_type, taxonomy og action. For sidelisten er id lik edit-page, base lik edit og post_type lik page, så én betingelse på post_type dekker listen, redigeringen og opprettelsen av et nytt element av samme type.
På dette objektet henger også to grensesnittelementer som ofte skrives fra bunnen av uten grunn: hjelpefaner (add_help_tab()) og skjermvalg (add_option(), for eksempel antall elementer per side i en liste). Begge bør legges til i action load-{$page_hook}, fordi skjermobjektet først finnes da, mens toppen med knappene “Hjelp” og “Visningsinnstillinger” ennå ikke er skrevet ut.
Hvilke body-klasser en skjerm i panelet får
admin-header.php bygger attributtet class på body-taggen fra flere kilder. Den første klassen er $hook_suffix der hvert tegn utenfor a-z0-9_- er byttet ut med en bindestrek, så en utvidelsesskjerm får klassen toplevel_page_min-side på body. Deretter kommer blant annet post-type-{type} og taxonomy-{taksonomi} fra skjermobjektet, admin-color-{skjema} fra brukerens profilinnstillinger, locale-{språk}, branch-{versjon} og version-{versjon}, multisite og network-admin i et nettverk, og wp-theme-{tema} med navnet på det aktive temaet. Egne klasser legges til med filteret admin_body_class. Slik kan CSS-en til en utvidelse treffe én skjerm med en klassevelger, uten å laste et eget stilark.
Hvordan kroker fungerer i WordPress-administrasjonspanelet
Kroker er mekanismen som endrer panelets oppførsel uten at man rører kjernefilene i WordPress.
Actions i administrasjonspanelet
Actions kjører kode på et bestemt tidspunkt:
// Legg til et skript bare i administrasjonspanelet
add_action('admin_enqueue_scripts', function($hook) {
// $hook inneholder identifikatoren til gjeldende side
if ($hook === 'toplevel_page_min-side') {
wp_enqueue_script(
'min-side-js',
plugin_dir_url(__FILE__) . 'js/admin.js',
['jquery'],
'1.0.0',
true
);
}
});
// Endre toppen av adminpanelet
add_action('admin_head', function() {
?>
<style>
#wpadminbar { background: #1d2327; }
</style>
<?php
});
// Legg til et varsel
add_action('admin_notices', function() {
?>
<div class="notice notice-success is-dismissible">
<p>Endringene dine er lagret.</p>
</div>
<?php
});Filtre i administrasjonspanelet
Filtre endrer data før de vises eller lagres:
// Endre tittelen på siden i panelet
add_filter('admin_title', function($admin_title, $title) {
return $title . ' | Mitt nettsted';
}, 10, 2);
// Endre teksten i bunnen av panelet
add_filter('admin_footer_text', function($text) {
return 'Laget med <a href="https://wordpress.org">WordPress</a> | Teknisk støtte: Mitt firma';
});
// Endre kolonnene i innleggslisten
add_filter('manage_posts_columns', function($columns) {
$new_columns = [];
foreach ($columns as $key => $value) {
$new_columns[$key] = $value;
if ($key === 'title') {
$new_columns['min_kolonne'] = 'Min ekstra kolonne';
}
}
return $new_columns;
});Hvilke CSS-klasser WordPress-administrasjonspanelet bruker
WordPress leverer et sett CSS-klasser som gir panelet et ensartet utseende.
CSS-klasser for beholdere i panelet
/* Hovedbeholder for siden */
.wrap {
margin: 10px 20px 0 2px;
}
/* Sideoverskrift */
.wrap h1 {
font-size: 23px;
font-weight: 400;
margin: 0;
padding: 9px 0 4px;
line-height: 1.3;
}
/* Metabokser */
.postbox {
min-width: 255px;
border: 1px solid #c3c4c7;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
background: #fff;
}
/* Skjemaer */
.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;
}CSS-klasser for knapper og grensesnittkomponenter
/* Knapper (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;
}
/* Varsler (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;
}Verdiene ovenfor er hentet fra filene buttons.css og common.css i versjon 7.1.3. Fargen på hovedknappen er ikke skrevet inn fast: den kommer fra CSS-variabelen --wp-admin-theme-color, som det valgte fargeskjemaet setter, og #3858e9 er bare reserveverdien. En utvidelse som skriver inn knappefargen fast i sitt eget stilark, vil skille seg ut i panelet til en bruker som har valgt et annet skjema i profilen. Det er tryggere å bruke kjerneklassene (button button-primary, notice notice-success) og ikke overstyre fargene deres.
Hvordan lage egne sider i administrasjonspanelet
Nedenfor er det korteste fungerende eksempelet på en innstillingsside bygget på Settings API. En fullstendig veiledning om rydding i menyer, kunderoller, fargeskjemaer og widgeter på kontrollpanelet finnes i en egen artikkel om tilpasning av WordPress-administrasjonen.
Hvordan legge til en enkel innstillingsside
class Min_Innstillingsside {
public function __construct() {
add_action('admin_menu', [$this, 'legg_til_side']);
add_action('admin_init', [$this, 'registrer_innstillinger']);
}
public function legg_til_side() {
add_options_page(
'Mine innstillinger',
'Mine innstillinger',
'manage_options',
'mine-innstillinger',
[$this, 'vis_side']
);
}
public function registrer_innstillinger() {
register_setting('mine_innstillinger_gruppe', 'mitt_valg_1');
register_setting('mine_innstillinger_gruppe', 'mitt_valg_2');
add_settings_section(
'min_seksjon',
'Hovedinnstillinger',
[$this, 'vis_seksjon'],
'mine-innstillinger'
);
add_settings_field(
'mitt_valg_1',
'Første valg',
[$this, 'vis_felt_1'],
'mine-innstillinger',
'min_seksjon'
);
}
public function vis_side() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('mine_innstillinger_gruppe');
do_settings_sections('mine-innstillinger');
submit_button('Lagre innstillingene');
?>
</form>
</div>
<?php
}
public function vis_felt_1() {
$verdi = get_option('mitt_valg_1');
?>
<input type="text" name="mitt_valg_1" value="<?php echo esc_attr($verdi); ?>" class="regular-text">
<?php
}
}
new Min_Innstillingsside();Hva som er forskjellen mellom admin-ajax.php og admin-post.php
Panelet har to separate innganger for forespørsler som ikke tegner en skjerm, og begge går utenom admin.php.
admin-ajax.php håndterer AJAX-forespørsler. Filen definerer konstanten DOING_AJAX, laster wp-load.php og panelets API, sender hodet X-Robots-Tag: noindex, utløser admin_init og deretter action wp_ajax_{action} for innloggede brukere eller wp_ajax_nopriv_{action} for gjester. Mangler parameteren action, ender det med svaret 0 og statuskode 400, og mangler en registrert behandler, ender det også med 0 og 400. På skjermene i panelet er adressen til denne filen tilgjengelig med en gang i JavaScript-variabelen ajaxurl, som admin-header.php skriver ut. Plugin Handbook beskriver hele flyten i kapittelet om AJAX i utvidelser.
admin-post.php er beregnet på skjemaer som sendes med vanlig POST og som skal følges av en omdirigering. Filen utløser admin_init og deretter admin_post_{action} for innloggede brukere eller admin_post_nopriv_{action} for gjester. Hvis ingenting er hektet på action, ender filen med et tomt svar og statuskode 400. Kroken er beskrevet på siden admin_post_{$action}.
Det de to filene har felles, får følger for ytelsen. admin_init kjøres ved hver forespørsel til admin-ajax.php, også ved de jevnlige forespørslene fra Heartbeat API (action heartbeat står på listen over kjernens actions, og for gjester finnes wp_ajax_nopriv_heartbeat). Tung kode hektet på admin_init uten betingelsen wp_doing_ajax() kjøres derfor ikke bare når en skjerm åpnes, men ved hvert slikt slag.
Hvordan panelet skiller seg fra malhierarkiet i temaet
Ordet “mal” i tittelen på denne artikkelen er lett å forveksle med temamaler. Forsiden velger fil etter malhierarkiet, som Theme Handbook beskriver slik:
“Når en besøkende lander på en hvilken som helst side på et WordPress-nettsted, bruker WordPress spørrestrengen til å avgjøre hvilken mal som skal brukes til å vise siden. Spørrestrengen inneholder data fra URL-en som hjelper til med denne avgjørelsen.”
Theme Handbook, Template hierarchy, egen oversettelse
For blokktemaer lister den samme dokumentasjonssiden opp tre steder der WordPress leter etter en mal, i prioritert rekkefølge: maler laget av brukeren og lagret i databasen som innleggstypen wp_template, mappen /templates i undertemaet og mappen /templates i foreldretemaet.
Panelet har ingen av disse mekanismene. Det finnes ingen reservefil som temaets index.php og ingen matching etter spørringstype. Hva som vises, avgjøres av filnavnet i wp-admin eller parameteren page, og oppsettet på skjermen er alltid den samme toppen, menyen og bunnen fra kjernen. Temaet har ingen innvirkning på hvordan panelet ser ut, bortsett fra at navnet havner i klassen wp-theme-* på body. Derfor legges stiler og skript for panelet til via admin_enqueue_scripts, og for forsiden via wp_enqueue_scripts. Blander man de to, ender adminstilarket på forsiden eller omvendt.
Hvordan sikre WordPress-administrasjonspanelet
Sjekke brukerens rettigheter
Sjekk alltid brukerens rettigheter:
// Sjekk om brukeren har riktige rettigheter
if (!current_user_can('manage_options')) {
wp_die(__('Du har ikke tilgang til denne siden.', 'text-domain'));
}
// Sjekk nonce for handlingen
if (!isset($_POST['min_nonce']) || !wp_verify_nonce($_POST['min_nonce'], 'min_handling')) {
wp_die(__('Ugyldig sikkerhetstoken.', 'text-domain'));
}Rense data fra skjemaer i panelet
// Rense tekst
$tittel = sanitize_text_field($_POST['tittel']);
// Rense HTML
$content = wp_kses_post($_POST['content']);
// Rense URL
$url = esc_url_raw($_POST['url']);
// Rense e-post
$email = sanitize_email($_POST['email']);Hvordan feilsøke WordPress-administrasjonspanelet
Hvordan slå på WP_DEBUG i panelet
// I wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', true); // Bruker ikke-minifiserte skriptDokumentasjonen Debugging in WordPress beskriver SCRIPT_DEBUG som en konstant som får WordPress til å bruke utviklingsversjonene av kjernens CSS- og JavaScript-filer i stedet for de minifiserte, og peker på den som nyttig når man tester endringer i de innebygde .js- og .css-filene. Standardverdien er false. I panelet betyr det at nettleseren får common.js i stedet for wp-admin/js/common.min.js, og at en stack trace fra konsollen peker på en lesbar linje i stedet for én lang linje i .min-filen. Den samme siden minner om at definisjonene må stå før kommentaren som avslutter redigeringen i wp-config.php.
Logge hendelser i panelet til debug.log
function log_admin_action($action, $data) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("Admin Action [{$action}]: " . print_r($data, true));
}
}
// Bruk
log_admin_action('user_login', ['user_id' => get_current_user_id()]);Hva du bør huske om WordPress-administrasjonspanelet
Fem punkter fra denne anatomien som teller når man skriver en utvidelse:
- Katalogstruktur: kunnskap om hvordan filene er organisert, gjør feilsøking og endringer raskere.
- Malsystem: callback-funksjoner og kroker styrer hvordan en skjerm tegnes.
- Sikkerhet: rettighetssjekk og rensing av data er grunnlaget.
- CSS-klasser: kjerneklassene holder skjermene til utvidelsen i tråd med resten av panelet.
- Feilsøking:
SCRIPT_DEBUGog feilsøkingsloggen korter veien til årsaken.
Endringer i panelet overlever oppdateringer bare når de bruker de innebygde mekanismene i stedet for å gå rundt dem. Slike endringer forbereder og tester vi på et stagingmiljø som en del av WordPress-utvikling.







