Summary
Every button a customer admin creates in Mobile App Manager → Buttons is persisted as an entry in a serialized array stored in the local-app-button-array* option family. This storage is one of the most-frozen surfaces in the codebase — customer sites have years of accumulated button configs depending on the existing schema.
Where it appears
| Option | Scope |
|---|---|
local-app-button-array |
Default / global button list |
local-app-button-array_subscriber |
Subscriber-role buttons |
local-app-button-array_administrator |
Admin-role buttons |
local-app-button-array_anonymous |
Anonymous-viewer buttons |
local-app-button-array_<custom_role> |
Custom-role variants |
mam-button-array* |
Modern alias (registered via MAM_Migration_Tasks aliasing) |
Reading either name returns the same value via the migration framework’s pre-option filters.
Storage shape
get_option( 'local-app-button-array_subscriber' );
// returns:
array(
array(
'id' => 'btn_42',
'title' => 'Edit listing',
'content_type' => 'open_form',
'class' => 'local_app_open_form',
'icon' => 'black_edit',
'visible' => 'on',
// per-button settings declared by the content class:
'require_login' => 'yes',
'pass_user_id' => 'yes',
'style_bg_color' => '#1A73E8',
// ... arbitrary keys the content class declared via app_settings()
),
array(
'id' => 'btn_43',
...
),
// ... up to N entries
);
The array order is significant — admin UI ordering corresponds to the array order. Reorder via the mam_app_settings_button_order filter.
Per-row keys
| Key | Purpose | Source |
|---|---|---|
id |
Unique button id within the array | mam-main (auto-generated on add) |
title |
Display title | admin |
content_type |
The button’s content class label (e.g., “Login Button”) | content class registration |
class |
The PHP class name (e.g., local_app_login_button) |
content class registration — frozen |
icon |
Icon asset name | admin / content class default |
visible |
on / off — show this button at all? |
admin |
style_* |
Per-button styling | admin |
<custom> |
Per-button settings declared by the content class via app_settings(environment => 'per-button') |
content class |
Why the option key is frozen
- ~2K customer sites have populated
local-app-button-array*with their config - Mobile apps in the field read the resulting JSON and assume the button shape
- Migration cost is high — renaming requires updating every site’s serialized array
Frozen contract — do not rename. A rename would require iterating every customer’s wp_options to copy + delete + verify.
Schema gotchas
- No normalized schema. A button missing a key your code expects produces a PHP notice. Always
?? $default. - Class names inside
classare frozen. Renaminglocal_app_login_buttonorphans every site’s saved login button. content_typeis a user-facing label. Changing it (e.g., from “Login Button” to “Sign in”) doesn’t break storage but changes what admins see.
Modifying button arrays programmatically
Read:
$buttons = get_option( 'local-app-button-array_subscriber', array() );
Or via the cascade-aware filter:
$buttons = apply_filters(
'mam_app_settings_get_buttons',
false, // default
'subscriber', // role
'main' // 'main' | 'left' | 'tab'
);
Write:
apply_filters(
'mam_app_settings_set_button',
$modified_button_blob,
'subscriber',
'btn_42'
);
Or for bulk reorder:
apply_filters(
'mam_app_settings_button_order',
$reordered_array,
'subscriber'
);
Or to delete:
apply_filters(
'mam_app_settings_delete_button',
'btn_42',
'subscriber'
);
⚠️ Don’t update_option directly with a hand-built array — the data manager runs validation and (where applicable) cache invalidation.
Related articles
- Settings cascade overview
- Per-button and per-role settings
- Content classes overview
- Frozen public contracts reference
- Hook: mam_app_settings_get_buttons
Metadata
| Field | Value |
|---|---|
| Article type | JSON Key Reference |
| Plugin slug | mam-main |
| Applies to plugin version | 2.1.11+ |
| Category | App Settings Reference |
| Audience | PHP developer |
| Last verified | 2026-05-02 |
