Signature
apply_filters( "mam_geodirectory_admin_settings_tab_content_{$tab}", string $html_line, string $tab );
| Parameter | Type | Description |
|---|---|---|
$html_line |
string | HTML accumulated so far for the page. Includes the page header and the tab nav. |
$tab |
string | The currently active tab key (sanitized via sanitize_key). |
Returns: string — the full HTML for the page. You must return it.
Purpose
Renders the body of a custom admin tab. The hook name is dynamic — it includes the active tab key — so a callback only fires for its own tab. Pair this with Hook: mam_geodirectory_admin_settings_tabs to register the tab key in the first place.
When this filter has any subscribers for the active tab, the page skips its built-in tab rendering for that tab. So you take complete responsibility for the tab body, including any forms, tables, or settings UI.
When it runs
Inside mam_geodirectory_admin_settings::create_admin_page():
$tab = sanitize_key( $_REQUEST['tab'] ?? 'gd-settings' );
$html_line = '<div class="local-app-main-content-area">';
$html_line .= mam_admin_settings::headline( __( 'GeoDirectory Settings', 'mam-suite' ) );
$html_line .= '<h4>...</h4>';
$html_line .= $this->add_page_tabs();
if ( has_filter( 'mam_geodirectory_admin_settings_tab_content_' . $tab ) ) {
$html_line = apply_filters( 'mam_geodirectory_admin_settings_tab_content_' . $tab, $html_line, $tab );
} elseif ( 'gd-claim-listings' === $tab ) {
/* render the built-in claim listings UI */
} else {
/* render the built-in settings UI */
}
The check is has_filter(), so the first subscriber wins — built-in rendering is skipped if anyone has subscribed for that key.
Example
add_filter( 'mam_geodirectory_admin_settings_tabs', function( $tabs ) {
$tabs['my-app-stats'] = 'Stats';
return $tabs;
} );
add_filter( 'mam_geodirectory_admin_settings_tab_content_my-app-stats', 'my_app_stats_body', 10, 2 );
function my_app_stats_body( $html_line, $tab ) {
$listings = wp_count_posts( 'gd_place' );
$html_line .= '<div class="tsl-admin-settings-holder">';
$html_line .= '<h3>Listing counts</h3>';
$html_line .= '<ul>';
$html_line .= '<li>Published: ' . (int) $listings->publish . '</li>';
$html_line .= '<li>Pending: ' . (int) $listings->pending . '</li>';
$html_line .= '<li>Draft: ' . (int) $listings->draft . '</li>';
$html_line .= '</ul>';
$html_line .= '</div>';
return $html_line;
}
Gotchas
- Built-in rendering is skipped entirely. If you subscribe and forget to render the body, the user sees an empty page.
$html_linealready includes the page header and nav. Append, don’t replace.- Escape user-controlled data. The page renders the result raw via
echo $html_line— useesc_html,esc_attr, and the WordPress nonce/cap helpers as you would on any admin page. - Capability check is upstream.
create_admin_page()already verifiescurrent_user_can('manage_options'). You can rely on that at the page level but add per-action checks (e.g., on form processing) yourself. - Forms within your tab need their own nonce. The plugin’s nonce
mam_geodirectory_settings_saveonly validates the global Settings tab’s form. For your own forms, use a distinct nonce.
Verification
This article was last verified against:
- Plugin:
mam-geodirectoryv2.1.5 - Source:
includes/mam_geodirectory_admin_settings.php—create_admin_page()
Re-verify whenever the page rendering structure changes (e.g., the conditional elseif chain), the has_filter() gating is replaced, or sanitize_key() is replaced as the tab-key sanitiser.
Related articles
- Plugin: mam-geodirectory
- Recipe: Configure the GeoDirectory settings page
- Hook: mam_geodirectory_admin_settings_tabs
- Hook: mam_geodirectory_admin_settings
Metadata
| Field | Value |
|---|---|
| Article type | Hook Reference |
| Plugin slug | mam-geodirectory |
| Applies to plugin version | 2.1.5+ |
| Category | Extending MAM Suite |
| Hook type | filter |
| Audience | PHP developer |
| Last verified | 2026-05-01 |
