Content source vs. global settings: where a button’s data comes from

The two inputs behind every button

Every button in a MAM app is backed by a content class — a handler that knows how to turn a nav button you’ve configured into something the mobile app can actually render (a Web URL button, a Login button, a Map button, a Phone Call button, and so on). When you place that button in the no-code builder and it shows up on a device, its data comes from two distinct places:

  1. Its content source — a value you set on this one button. The URL a Web URL button opens. The page a WP Content button renders. This is per-instance: change it on one button and only that button changes.
  2. Global settings — site-wide values that style or gate buttons of that type everywhere in the app. The login screen’s background color. Whether login is required at all. Change one of these and every button of that type, across every screen, changes at once.

Knowing which one you’re touching is the difference between changing one button and changing the whole app. This article walks through where that line falls, why it’s drawn there, and how the two inputs come back together once the app is built.


Why the split exists

A no-code builder has to serve two competing needs. Some things genuinely belong to one button — the specific URL a tile opens means nothing to any other button. Other things belong to the app’s identity — its login experience, its color palette, the rules about who has to sign in. Picture the alternative: if the login screen’s background color were a per-button value, you’d have to set it identically on every login entry point and keep them all in sync by hand, forever. If a button’s destination URL were global instead, you could only ever have one Web URL button in the whole app.

So MAM keeps the two apart on purpose. Per-instance data lives with the button; site-wide data lives with the app. You can edit both — just from different places — and under the hood, the platform stores them in different shapes.


How a content class declares which is which

Each content class lists the settings fields it offers through its app_settings() method, and every field carries an environment marker. Only one value of that marker actually changes a field’s scope:

  • environment => 'global' — a site-wide setting: its value gets folded into the app-wide payload and applies to every button of this type. The other markers you’ll run into in the code — all, backend, hidden — only control where and whether the field shows up in the admin UI; they don’t affect app-wide scope. Anything that isn’t global stays a per-role value. (And there’s no per-button marker in the code, whatever some inline comments might suggest.)

The per-instance content source works completely differently — it isn’t an environment value at all. It’s the button’s own source, stored on the button record and carried along its own path: into the edit form as get_content_type_form( $id, $content_source ), and back out to the device as get_data_for_app( $source ).

You can see the per-instance side most clearly in the simplest content classes. A Web URL button’s entire edit form is just a single text input for its content source, and when the app payload gets built, that value is passed straight through to the device. There’s no global styling to reconcile here — the button is its content source.

The Login button sits at the opposite end of the spectrum. Roughly forty of the fields it exposes — “Require Login?”, “Allow user to sign up with Email”, the links text color, the button background color, the screen background color, the background image — are declared environment => 'global'. None of these belong to one login tile; they belong to the app’s login experience as a whole. Set them once, and they apply wherever a login button shows up.


The mental model: content source is the “what,” global settings are the “how”

Here’s a shorthand for holding the two apart:

Content source (per-instance) Global settings (site-wide)
Scope This one button Every button of this type
Edited where On the button itself, in its edit form In the app-wide settings for that content type
Typical examples A Web URL’s destination, a WP Content button’s target page, a Phone Call button’s number Login screen colors, “Require Login?”, sign-up methods, shared theming
Stored as A value attached to the button’s record A site-wide value (the global layer)
If you change it Only this button changes The whole app changes

The content source answers what this button points at. The global settings answer how buttons of this kind look and behave across the app. Most content classes lean toward one side or the other: navigation-style buttons (Web URL, Phone Call, WP Content) are mostly content source with barely any global styling, while experience-style buttons like Login are mostly global settings with little or no per-button source.


How the two come back together

You edit these two things in different places, but the app only ever receives one merged result. When the app’s data payload gets built, the platform walks through each button and asks its content class for whatever data the device needs. Per-instance content sources are read straight off the button and passed through; global settings are read from the site-wide options and folded into that same payload — so by the time a button reaches the device, it’s already styled and gated.

There’s also a fallback built into this resolution. When a more specific value is missing — say, a per-role or per-button override — the platform falls back to the global setting instead of rendering nothing at all. This cascade is handled centrally, as the payload is assembled, so you never have to wire it up per button. In practice, that means a global setting acts as the default a button inherits unless something more specific overrides it.

Because global values are shared and inherited this way, two things follow from that:

  • Editing a global setting is a broad change. It touches every button of that type — and that’s the feature, not a side effect. But it does mean “fix the color on this login button” and “change the app’s login background color” are actually the same action.
  • Editing a content source is a narrow change. It’s local to one button, which is exactly why you set it on the button itself.

Where this connects

  • The per-instance vs. site-wide split is one layer of a larger settings cascade that also includes per-role values. See Settings cascade: global, per-role, per-button and App settings model: roles, environments, path to phone.
  • The content classes that declare these fields are described in Content classes vs. elements vs. tab-bar buttons and How a MAM app is assembled: classes, buttons, screens, tab bar.
  • For how both inputs end up in the single JSON the device consumes, see The phone-data pipeline and Data model: the mobile client consumes one JSON payload.

Verification

This article was last grounded against:

  • mam-main/includes/content-classes/README.md — the environment marker and the content-class interface
  • mam-main/includes/content-classes/local-app-web-url-class.php — a pure content-source example (get_content_type_form, get_data_for_app)
  • mam-main/includes/content-classes/local-app-login-class.php — a settings-heavy example with many environment => 'global' fields
  • mam-main/includes/app-settings/local-settings.php and README.md — how global settings are folded into the payload and how the cascade falls back to a global value

Re-verify if the environment marker vocabulary changes (today the values in code are global, all, backend, and hidden), if the content-source plumbing in get_content_type_form/get_data_for_app is reworked, or if the cascade fallback behavior moves out of the data manager.

Was this article helpful?
Contents

    Need Support?

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