Define settings fields for a custom button type

Goal

Give your custom button type its own settings tab in the Mobile App Manager, populated with the fields an admin needs to configure it. You do this by implementing two methods on your content class: app_settings_categories() (the tabs) and app_settings() (the fields inside those tabs).

This is a How-to. It assumes you already have a content class registered and rendering. If you do not yet have one, start with How-to: Add a new content class (custom button type) and come back here to give it settings.

Scope note: this article covers the schema you declare — the array shapes the settings UI reads. It does not cover writing a brand-new field widget (a type the renderer has never seen). Use the existing type values listed below unless you are also extending the renderer.


Prerequisites

  • A working MAM Suite dev environment with mam-main active.
  • A registered content class under mam-main/includes/content-classes/ (or in your own plugin) that already implements the duck-typed content-class interface. See the content-classes README at mam-main/includes/content-classes/README.md.
  • Familiarity with PHP and the WordPress options model.
  • A reference implementation to copy from: mam-main/includes/content-classes/local-app-login-class.php is the cleanest worked example of a full settings schema.

How the pieces fit together

When the admin opens your button type in the builder, the settings UI calls two methods on your class:

  1. app_settings_categories() returns the tabs. Each tab is an array with a title (what the admin sees) and a slug (the internal handle).
  2. app_settings() returns the fields. Each field declares which tab it belongs to (via its category, which must match a tab slug), what widget renders it (type), and the option key its value is stored under (variable).

The settings infrastructure that consumes these arrays lives in mam-main/includes/app-settings/. When a field’s environment is global, its saved value is copied into the mobile JSON payload the app downloads (see app-settings/local-settings.php), which is how a setting an admin types in the dashboard ends up changing the app.


Steps

1. Declare your settings tab(s)

Implement app_settings_categories(). Return one entry per tab. Most button types need only one.

public function app_settings_categories() {

    $cat_array[] = array(
        'title' => 'My Button Settings',  // admin-facing tab label
        'slug'  => 'my_button',           // internal handle, referenced by each field
    );

    return $cat_array;
}

The slug is the join key. Every field you define in the next step points back to a tab by setting its category to one of these slugs. Pick a slug that is unlikely to collide with another type’s (the login button uses login, the home-for-tabbar button uses content).

2. Define your fields in app_settings()

Implement app_settings(). Append one array per field and return the collected array. Each field array supports these keys:

Key Required Purpose
category Yes Must match a tab slug from step 1. Routes the field to a tab.
title Yes The field label the admin sees. Wrap in __( ..., 'mam-suite' ) for translation.
variable Yes The option-key handle the value is saved and read under. Treat this as frozen once shipped — see the warning below.
type Yes Which input widget renders. See the type table below.
id Yes The DOM id for the field element. Conventionally tsl-setting-<variable> (some yes/no fields use tsl-<variable>). Keep it unique on the page.
environment Yes Controls where the value applies and whether it ships to the app. See the environment table below.
default No Default value when nothing is saved. Common for color (e.g. #000000) and yes-no.

A minimal two-field example, modeled on local-app-login-class.php:

public function app_settings() {

    $settings_array[] = array(
        'category'    => 'my_button',
        'title'       => __( 'Require Login?', 'mam-suite' ),
        'variable'    => 'my_button_require_login',
        'type'        => 'yes-no',
        'id'          => 'tsl-my_button_require_login',
        'environment' => 'global',
    );

    $settings_array[] = array(
        'category'    => 'my_button',
        'title'       => __( 'Header Background Color', 'mam-suite' ),
        'variable'    => 'my_button_header_color',
        'type'        => 'color',
        'id'          => 'tsl-setting-my_button_header_color',
        'environment' => 'global',
        'default'     => '#000000',
    );

    return $settings_array;
}

3. Choose the right type for each field

type selects the input widget and, for some values, post-processing of the saved value before it reaches the app. The widely used, safe-to-reuse values are:

type Renders / stores
yes-no A yes/no toggle. Saved as the string yes or no.
text Single-line text.
image Media-library image picker; stores the image URL. Image fields also get width/height/aspect derived for the payload.
color Color picker; pair with a default.

These four cover almost every field in the shipping content classes. There is no plain textarea, select, or number type — a field whose type is not one the renderer knows renders nothing and logs an “Unknown field type” admin notice.

The renderer also recognizes specialized types used by core buttons: the text variants startext and rgbtext (a text input plus custom post-processing), text_json (a JSON textarea), the dropdown family (dropdown, black-white, and friends, whose options are supplied in code, not by you), and bespoke ones like home-screen-stack. These carry custom serialization or custom UI and are not general-purpose — only use them if you are deliberately reusing that exact behavior and have read how the type is rendered in app-settings/admin-settings-page.php (create_variable_option()) and post-processed for the payload in app-settings/local-settings.php. For a new button type, stay with the common types above.

4. Set environment deliberately

environment controls scope and whether the value is delivered to the app:

environment Behavior
global Site-wide setting. When saved, the value is written into the mobile JSON payload the app downloads (see local-settings.php). Use this for anything the app needs to read.
all Used by core for settings shown across contexts. Inspect how a core button using all (e.g. the home-for-tabbar class) behaves before adopting it.
hidden The field is part of the schema but not shown as a normal input — used for values managed by custom UI rather than typed directly.

The content-classes README documents a per-button environment for per-instance (rather than site-wide) values, but no shipping content class uses it — the concrete values in the shipping classes are global, all, and hidden. If you need true per-button-instance storage, verify the exact handling against the current app-settings/ code before relying on the per-button keyword.

5. Verify the tab renders and values round-trip

  1. Reload the Mobile App Manager and open your button type. Confirm your tab appears with its title and that each field renders with the expected widget.
  2. Enter values and save. Confirm they persist on reload (read back under the variable handle).
  3. For global fields, regenerate / inspect the app payload and confirm the value appears under the expected key. If a saved value is missing from the payload, the most common cause is an environment that is not global, or an empty value being stripped before delivery.

Gotchas

  • variable is effectively frozen once a customer has saved a value. It is the option-key handle the value lives under. Renaming it on a live button type orphans every site’s saved setting. Choose a clear, prefixed name up front (e.g. my_button_*) and rename later only with a migration.
  • category must exactly match a tab slug. A field whose category matches no tab will not render. This is a silent failure — double-check the spelling.
  • id must be unique on the settings page. Duplicate DOM ids break the field UI and JS handlers. The tsl-setting-<variable> convention keeps them unique for free.
  • Empty values may be dropped. The payload builder skips empty/zero-length values for delivery, so do not rely on an empty string reaching the app — use a meaningful default instead.
  • No interface enforcement. Like the rest of the content-class interface, a typo in a key name fails only at render/save time, not at load. Test the round-trip.

  • How-to: Add a new content class (custom button type) — registering the class these settings attach to.
  • Content-classes reference: mam-main/includes/content-classes/README.md (Class Interface, Settings Schema).
  • App-settings infrastructure: mam-main/includes/app-settings/ (local-settings.php, global-settings.php).
  • Worked example: mam-main/includes/content-classes/local-app-login-class.php.

What’s next

Once your fields save and (for global fields) reach the app payload, wire them into your class’s get_data_for_app() so the button actually uses the configured values when it renders on the device.

Was this article helpful?
Contents

    Need Support?

    Can't find the answer you're looking for? Don't worry we're here to help!