> For the complete documentation index, see [llms.txt](https://playground.e107sk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://playground.e107sk.com/plugin-dev-quick-notes/how-to-1/theme-preferences.md).

# Theme preferences

Theme preferences are admin-editable values that belong to the theme rather than to any plugin — section headings, subtitles, layout switches, feature toggles. They are edited under **Admin → Theme → Aragorn → Configure** and read in layout templates through the `{THEME_PREF}` shortcode.

Use a theme preference when the value is *chrome*: a single string or setting that shapes how a section is presented. Use Featurebox, Custom Pages or News when the value is *content*: repeatable, ordered, or authored over time.

Preferences are stored in their own row of the `core` table (`theme_aragorn`), not in the site-wide pref blob. Uninstalling or switching the theme leaves them untouched.

***

### Adding a preference

#### 1. Declare the field

Add an entry to `config()` in `theme_config.php`. The array key is the pref name you will use in templates.

```php
public function config()
{
    return array(

        'features_subtitle' => array(
            'title'      => defset('LAN_THEME_PREF_FEATURES_SUBTITLE', 'Features: subtitle'),
            'type'       => 'text',
            'multilan'   => true,
            'writeParms' => array(
                'size'      => 'xxlarge',
                'maxlength' => 250,
            ),
            'help'       => defset('LAN_THEME_PREF_FEATURES_SUBTITLE_HELP', 'Short line shown under the Features heading.'),
        ),

    );
}
```

Each field array is passed straight to `e_form::renderElement()`, so any field type and write parameter that works in a plugin admin UI works here too.

| Key          | Purpose                                                                            |
| ------------ | ---------------------------------------------------------------------------------- |
| `title`      | Label in the left column of the config table. Required.                            |
| `type`       | `text`, `textarea`, `dropdown`, `boolean`, `image`, `method`, …                    |
| `multilan`   | `true` stores one value per language. See below.                                   |
| `writeParms` | Passed to the form element — `size`, `maxlength`, `optArray`, `default`, `post`, … |
| `help`       | Help text rendered under the field.                                                |

#### 2. Add the label to the admin language file

`e107_themes/aragorn/languages/English_admin.php`

```php
define('LAN_THEME_PREF_FEATURES_SUBTITLE', 'Features: subtitle');
define('LAN_THEME_PREF_FEATURES_SUBTITLE_HELP', 'Short line shown under the Features heading.');
```

Front-end strings stay in `languages/English.php`. The two files are loaded by different code paths and neither one implies the other.

> **Why the split, and why `e107::themeLan('admin', basename(__DIR__), true)` instead of `e107::lan('theme', 'admin', true)`?** Core themes use the short form, but it resolves through the `THEME` constant, which points to the **admin** theme while you are in the admin area. That works for `bootstrap3` only because it doubles as the default admin theme. A front-end-only theme has to name itself explicitly.

#### 3. Use it

Nothing else is required — no install step, no upgrade routine. A pref that has never been saved simply reads as empty.

***

### Using a preference in a layout

```html
<!-- short form -->
{THEME_PREF=features_subtitle}

<!-- with a fallback for the not-yet-configured case -->
{THEME_PREF: name=features_subtitle&default=What we offer}

<!-- inside an HTML attribute -->
<section aria-label="{THEME_PREF: name=features_subtitle&filter=attribute}">

<!-- plain text, tags stripped -->
<meta name="description" content="{THEME_PREF: name=features_subtitle&filter=text}">
```

A typical section:

```html
<div class="container-xl py-5">
    <div class="text-center mb-4">
        <h2 class="section-title">{THEME_PREF: name=features_title&default=Features}</h2>
        <p class="section-subtitle text-secondary">{THEME_PREF: name=features_subtitle&default=What we offer}</p>
    </div>

    {FEATUREBOX|category=features}
</div>
```

#### Parameters

| Parameter | Default | Description                                                        |
| --------- | ------- | ------------------------------------------------------------------ |
| `name`    | —       | Pref key as declared in `config()`. Required.                      |
| `default` | empty   | Returned when the pref is unset or empty for the current language. |
| `filter`  | `html`  | Output context — see below.                                        |

| `filter`    | Output                                                                                |
| ----------- | ------------------------------------------------------------------------------------- |
| `html`      | Bbcode and constants parsed, emotes and auto-linking off (`e_parse` `TITLE` context). |
| `text`      | Tags stripped, plain text.                                                            |
| `attribute` | Escaped for use inside an HTML attribute.                                             |

The short form `{THEME_PREF=key}` accepts only the pref name. Any parameter means the colon form `{THEME_PREF: a=b&c=d}` — that is the only syntax e107 parses into an array.

#### From PHP

```php
$subtitle = e107::getScBatch('theme')->sc_theme_pref(array(
    'name'    => 'features_subtitle',
    'default' => 'What we offer',
));
```

Or read the raw value, bypassing the parser — in which case escape it yourself:

```php
$value = e107::getThemePref('features_subtitle');
```

***

### Multilingual fields

With `'multilan' => true` the value is stored as `array(e_LANGUAGE => value)`. The language currently selected **in the admin area** is the one being edited, so switching admin language and saving again adds a second translation rather than overwriting the first.

`{THEME_PREF}` resolves the active front-end language automatically. A language with no value saved yet falls through to `default=`.

Multilan is worth it for anything a visitor reads. Skip it for switches, class names and other values that are the same in every language.

***

### Notes and gotchas

**An empty pref does not remove its wrapper.** `{THEME_PREF}` returns an empty string, but the surrounding `<p>` stays in the markup. Either supply a sensible `default=`, or give the section its own shortcode that returns the wrapper too.

**`+` in `default=` becomes a space.** The colon syntax is parsed with `parse_str()`. Write `%2B` for a literal plus. Spaces themselves are fine.

**`&` vs `&amp;`** — both work. e107 normalises the entity before parsing parameters.

**Constants as values.** A stored value shorter than 35 characters that matches the name of a defined constant is replaced by that constant's value. This makes `LAN_THEME_FEATURES_SUB` a valid thing to type into the field, but it also means a subtitle that happens to read `SITENAME` will not survive verbatim.

**Escaping is not optional.** e107 does not run theme preferences through `toDB()` on save — the posted value is written to the pref object as-is. Anyone with the `TMP` admin permission can therefore store raw markup. Always output through `{THEME_PREF}` or `$tp`, never `echo` the raw pref.

**No `init()` in the theme shortcode batch.** e107 skips `init()` for `theme_shortcodes` specifically, so the plugin pattern of caching prefs there silently does nothing. `{THEME_PREF}` reads per call instead; the pref object is already loaded, so there is no extra query.
