# Admin settings

> Webtrees module settings

---

At the top of the admin page, you can access the administrative [Cross-Reference Overview](../features/enhanced-links/xref-overview) and the [Renumber xref_IDs (with references)](../features/enhanced-links/renumber) function. \
You can also enable or disable the basic features:

1. [Enhanced Links](../features/enhanced-links)
2. [Markdown enhancements](../features/markdown)
3. Help functions ([German Webtrees Manual](../features/wthb))
4. [Home Link](../features/home-link)
5. [UID Lookup](../features/uid-lookup)

When the first four basic features are enabled, more detailed settings can be configured further down. There are also two global settings for debug output in the browser’s JavaScript console and the web server error log file, and you can choose whether links should always open in a new browser tab (it’s also possible to set different options for each core feature).

Below are the available options on the admin page:

[![admin page](img/le/screenshot_admin-page_small.png)](/img/le/screenshot_admin-page.png)

## [1] Enhanced Link Support

### Custom external targets

Here are two example entries from [predefined targets](https://codeberg.org/bschwede/linkenhancer/src/branch/main/resources/js/index-le.js) to illustrate the principle who to provide **additional external targets** with an own custom JavaScript object.

<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;fsft&#34;</span><span style="color:#91d7e3;font-weight:bold">:</span> {
</span></span><span style="display:flex;"><span>        name<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;Family Search Tree - $ID&#39;</span>,
</span></span><span style="display:flex;"><span>            url<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;https://www.familysearch.org/tree/person/details/&#39;</span>,
</span></span><span style="display:flex;"><span>                cname<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;icon-fsft&#39;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;wp&#34;</span><span style="color:#91d7e3;font-weight:bold">:</span> {
</span></span><span style="display:flex;"><span>        name<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;Wikipedia&#39;</span>,
</span></span><span style="display:flex;"><span>            url<span style="color:#91d7e3;font-weight:bold">:</span> (id, title) =&gt; {
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">let</span> parts <span style="color:#91d7e3;font-weight:bold">=</span> id.split(<span style="color:#a6da95">&#39;/&#39;</span>);
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> (parts.length <span style="color:#91d7e3;font-weight:bold">&lt;</span> <span style="color:#f5a97f">2</span>) {
</span></span><span style="display:flex;"><span>                    title <span style="color:#91d7e3;font-weight:bold">=</span> title <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; - &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> options.i18n(<span style="color:#a6da95">&#39;syntax error&#39;</span>) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34;!&#34;</span>;
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">return</span> { url<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;&#39;</span>, title };
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">let</span> subdomain <span style="color:#91d7e3;font-weight:bold">=</span> parts[<span style="color:#f5a97f">0</span>];
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">let</span> article <span style="color:#91d7e3;font-weight:bold">=</span> parts.slice(<span style="color:#f5a97f">1</span>).join(<span style="color:#a6da95">&#39;/&#39;</span>);
</span></span><span style="display:flex;"><span>                title <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">`</span><span style="color:#a6da95">${</span>title<span style="color:#a6da95">}</span><span style="color:#a6da95"> - `</span> <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">decodeURIComponent</span>(article) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">` (</span><span style="color:#a6da95">${</span>subdomain<span style="color:#a6da95">}</span><span style="color:#a6da95">)`</span>;
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">return</span> { url<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`https://</span><span style="color:#a6da95">${</span>subdomain<span style="color:#a6da95">}</span><span style="color:#a6da95">.wikipedia.org/wiki/</span><span style="color:#a6da95">${</span>article<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>, title };
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>                cname<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;icon-wp&#39;</span>,
</span></span><span style="display:flex;"><span>                    help<span style="color:#91d7e3;font-weight:bold">:</span> [
</span></span><span style="display:flex;"><span>                        { n<span style="color:#91d7e3;font-weight:bold">:</span> options.i18n(<span style="color:#a6da95">&#39;wp-help1&#39;</span>), e<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;de/Webtrees&#39;</span> },
</span></span><span style="display:flex;"><span>                        { n<span style="color:#91d7e3;font-weight:bold">:</span> options.i18n(<span style="color:#a6da95">&#39;wp-help2&#39;</span>), e<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;en/Webtrees&#39;</span> },
</span></span><span style="display:flex;"><span>                        { n<span style="color:#91d7e3;font-weight:bold">:</span> options.i18n(<span style="color:#a6da95">&#39;wp-help3&#39;</span>), e<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;other-subdomain/Webtrees&#39;</span> },
</span></span><span style="display:flex;"><span>                    ]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

**Property explanation** (the syntax of a base [enhanced link](../features/enhanced-links): `#@param-key=param-value`):

- *key* — the short name or query parameter key (param-key)
- *name* — title or label to be displayed as link title; the placeholder `$ID` is replaced by the given id (param-value)
- *url* — service URL to be called; the parameter value / given record id (param-value) is appended to the end of the URL. It can also be a function that accepts `(id, title)` and returns an object `{ url, title }`.
- *cname* — CSS class name(s) whitespace separated
- *help* — optional array of objects `[{n:'', e:''},..]` to illustrate the use of a non-standard target by examples (in `e`), provided with explanatory text (in `n`). Listed in the markdown help.

`options.i18n` is a function passed through from this module in order to provide translated strings.

Any CSS rules required are best added via the [CSS and JS module](https://wiki.genealogy.net/Webtrees_Handbuch/Anleitung_f%C3%BCr_Administratoren/Module#CSS_und_JS). Only the definition of the icon (size 30 x 30 pixels is sufficient) as a background image is actually needed — referencing as [data: URLs](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data). For example: `.icon-whatever { background-image: url(...) }`

## [2] Markdown enhancements

If you as a developer also want to apply the [markdown editor](../features/markdown/mde) (mde) to text areas on the edit pages of other custom modules, you can do so using the `MarkdownEditorActivationService` (available since v1.2.11 — details see [#101](https://codeberg.org/bschwede/linkenhancer/issues/101)):

<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">&lt;?</span>php
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">private</span> <span style="color:#c6a0f6">function</span> <span style="color:#8aadf4">toggleTinyMde</span>(bool <span style="color:#f4dbd6">$enable</span>, bool <span style="color:#f4dbd6">$force</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">false</span>)<span style="color:#91d7e3;font-weight:bold">:</span> void
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$class</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;Schwendinger</span><span style="color:#8aadf4">\\</span><span style="color:#a6da95">Webtrees</span><span style="color:#8aadf4">\\</span><span style="color:#a6da95">Module</span><span style="color:#8aadf4">\\</span><span style="color:#a6da95">LinkEnhancer</span><span style="color:#8aadf4">\\</span><span style="color:#a6da95">Services</span><span style="color:#8aadf4">\\</span><span style="color:#a6da95">MarkdownEditorActivationService&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (Registry<span style="color:#91d7e3;font-weight:bold">::</span><span style="color:#8aadf4">container</span>()<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#8aadf4">has</span>(<span style="color:#f4dbd6">$class</span>)) {
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d">/** @var MarkdownEditorActivationService $mde_service */</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$mde_service</span> <span style="color:#91d7e3;font-weight:bold">=</span> Registry<span style="color:#91d7e3;font-weight:bold">::</span><span style="color:#8aadf4">container</span>()<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#8aadf4">get</span>(<span style="color:#f4dbd6">$class</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$existingRule</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f4dbd6">$mde_service</span><span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#8aadf4">getCustomRule</span>(self<span style="color:#91d7e3;font-weight:bold">::</span><span style="color:#8aadf4">CUSTOM_TITLE</span>);
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$force</span> <span style="color:#91d7e3;font-weight:bold">||</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$enable</span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#91d7e3;font-weight:bold">!</span><span style="color:#f4dbd6">$existingRule</span> <span style="color:#91d7e3;font-weight:bold">||</span>
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3;font-weight:bold">!</span><span style="color:#f4dbd6">$enable</span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#f4dbd6">$existingRule</span>
</span></span><span style="display:flex;"><span>        ) {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$mde_service</span><span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#8aadf4">setCustomRule</span>(
</span></span><span style="display:flex;"><span>                self<span style="color:#91d7e3;font-weight:bold">::</span><span style="color:#8aadf4">CUSTOM_TITLE</span>,  <span style="color:#6e738d;font-style:italic">// module name as key
</span></span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$enable</span> <span style="color:#91d7e3;font-weight:bold">?</span> [<span style="color:#a6da95">&#34;source-transcription-detail&#34;</span>] <span style="color:#91d7e3;font-weight:bold">:</span> [], <span style="color:#6e738d;font-style:italic">// handler: route name
</span></span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$enable</span> <span style="color:#91d7e3;font-weight:bold">?</span> [<span style="color:#a6da95">&#34;textarea[id$=&#39;_text&#39;]&#34;</span>] <span style="color:#91d7e3;font-weight:bold">:</span> [] <span style="color:#6e738d;font-style:italic">// filter: querySelector
</span></span></span><span style="display:flex;"><span>            );
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

This only works if this feature has been enabled globally and the edit page is associated with a tree that also uses Markdown in its notes.

You can view the registered users in the hidden text field below the radio buttons for enabling or disabling the option *“Markdown editor for note textareas ”* by hovering your mouse cursor over the area.

Known custom modules that make use of this feature:

- [Source Transcriptions](https://github.com/hartenthaler/hh_source_transcription)
- [Ortsregister](https://github.com/thobgg/webtrees-ortsregister)

## [3] Webtrees manual

It is possible to import routes registered in webtrees on demand. This makes it easier to cover individual custom module configurations.

Furthermore you can import and export data in CSV format in order to make changes more convenient. You can define the separator and for import the character encoding (on export it is always UTF-8). You can see at a glance how many data rows are stored in the table and how many of them have a URL assigned.

![admin page data table](img/le/screenshot_admin-page-data-table.png)

In case of discrepancies the context help can be more easily adjusted by activating the option *"Output of debug information on the JS console"*. This provides you with information on the active route and, if applicable, data table entries found for matching routing to help topics.

### Route-to-help mapping

The **mapping of routes to help articles** in the manual is stored in the [database table](../dev/project-structure) `le_route_help_map`. This module comes with predefined mapping rules. If there is something missing or you find an issue, don't hesitate to share it.

**Technical background information** follows. The table has the following headers (the headers required in a CSV file for import are marked with !!):

- `id`: automatic key — not relevant, the data field is only used to make it easier to identify the data record during editing.
- `path` **!!**: route path — corresponds to the path of webtrees pretty URLs
- `handler` **!!**: usually corresponds to the PHP class name of the code that handles the request
- `method` **!!**: web request method (GET, POST, HEAD). Only GET routes are generally relevant for assignment to manual sections.
- `extras` **!!**: PHP class name of access level (Fisharebest\Webtrees\Http\Middleware\Auth*)
- `subcontext` **!!**: CSS selector string or JSON object string to address a component/subcontext topic on the given page
- `category`: string value for better grouping data rows; only value 'generic' has a special meaning
- `order` **!!**: arbitrary numerical sort key, matching data rows are sorted in ascending order (standard value is 10)
- `url` **!!**: path of the URL to the webtrees manual (concatenated with the given GenWiki base/domain URL). Also fully qualified URLs to other web resources are possible and supported.
- `updated_at`: timestamp of last update — helps to identify possibly outdated data rows

As we can see in [app/Http/Routes/WebRoutes.php](https://github.com/fisharebest/webtrees/blob/main/app/Http/Routes/WebRoutes.php), where standard webtrees routes are defined, for a route the following information is mandatory:

1. `path`
2. `handler`
3. `method`

Optionally `extras` can be provided. If none of those four mentioned fields is set, the data row is filtered by the [seeder](https://codeberg.org/bschwede/linkenhancer/src/branch/main/src/Schema/SeedHelpTable.php). On import `category` and `order` are set automatically if not set.

In order to get a first class match with the current route, those four fields have to match.

Normally, it is only necessary to assign GET routes to an article. We also have a few redirect routes and ajax helper routes (which provide data for form controls) that can be ignored.

Special cases are the `path`s beginning with `/module/{module}/{action}` or `/tree/{tree}/data-fix/{data_fix}`: they are handled by an integrated proxy class that addresses the custom module by a route attribute:

- [ModuleAction class](https://github.com/fisharebest/webtrees/blob/main/app/Http/RequestHandlers/ModuleAction.php): attribute `module`
- [DataFixPage class](https://github.com/fisharebest/webtrees/blob/main/app/Http/RequestHandlers/DataFixPage.php): attribute `data_fix`

For mapping such a route to a specific custom module help article the `handler` is set to the above mentioned route attribute value (e.g. `_vesta_classic_look_and_feel_`). For a more generic rule, `path` can also be empty (then one rule can be used for both cases).

Fallback rules for access levels are matched by `category='generic'` and the specific Auth classname in `extras`.

In ascending order the first rule where `subcontext = ''` is selected for the top menu link. If nothing else applies, the link points to the start page of the manual.

If activated, `subcontext` topics on a page can be provided with an additional help link. You can set a CSS selector string or a JSON object string with the following properties:

- `p`: position of popover, optional (default is top, other possible values: left, right, bottom)
- `f`: css selector/filter string (only necessary if you want the popover to be positioned differently) OR
- `e`: javascript expression string

This is useful if there are components on the page that require more detailed information than offered by the help link in the top menu (e.g. for custom module functions on the individuals page). This help link is displayed in a popover (because an additional link does not always work in bootstrap tabs, accordion headers or menu titles).

Set `category='generic'` for rules that should apply to more than one page.

### Table of contents (TOC)

This module downloads a multilingual **table of contents** for the German webtrees manual from a remote feed (JSON files: structure, language translations, and a version manifest). The data is cached locally and refreshed automatically:

- **On-the-fly**: when a user views the TOC page and the cache has expired (24 h), the service checks the feed manifest (`version.json`) for per-file hash changes and re-downloads only the files that changed.
- **Proactive (cron)**: the `refresh-wthb-toc` cron job (daily at 03:00 by default) downloads all files regardless of cache state.

**Auto-update toggle**: the module admin page offers an "Auto-update" switch for the TOC data. When disabled, only locally cached data is served (no HTTP downloads). The admin **Force refresh** button and the CLI with `--force` still work regardless of the switch. Tip: refresh the data before disabling to ensure the latest version is cached.

**CLI**:

```
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php              # scope from module pref
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --all        # all available languages
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --lang=en,fr # specific languages
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --clear      # only clear cache
php modules_v4/linkenhancer/cli/refresh-wthb-toc.php --force      # run even if auto-update is disabled
```

While auto-update is switched off, scheduled refreshes are skipped (the cron run exits 0 without doing work). `--force` refreshes anyway (e.g. to prepare for re-enabling), and `--clear` works regardless of the switch.

### External help links

The module contains some standard **external help links**, that are defined in JSON — array of objects with the following members:

- `title`: display text for the link, can also contain HTML (string, mandatory), language should be en-US; for standard link titles translations are provided
- `url`: link to web resource (string, mandatory)
- `class`: additional CSS class names (string, optional)
- `self`: if set, link `target` is `_self` (opens in place), otherwise `_blank` (any type, optional)
- `i18n`: overwrite title with user defined translation for given language tags (object, optional; keys are language tags, values are translated titles)

Example:

```json
[
  {"title":"webtrees FAQ <i class=\"fa-solid fa-circle-question\"></i>",
   "url":"https://webtrees.net/faq/",
   "i18n": { "de": "webtrees Häufig-gestellte-Fragen <i class=\"fa-solid fa-circle-question\"></i>" }
  }
]
```

## [4] Home link

Dynamic CSS styles can be configured with a [JSON](https://en.wikipedia.org/wiki/JSON) object:

```javascript
{ 
  "*": ".homelink { color: #039; }",
  "colors_nocturnal": ".homelink { color: antiquewhite; }"
}
```

**Explanation:** The object key name is set to the theme name to be matched, and the string value contains one or more CSS style rules. In this example we have a fallback rule with key `*` that matches all themes unless there is a specific key.

Theme *colors* is a special case, where we also have palettes. In order to address this combination the key is set to `theme name_palette name`. The key *colors* matches all subthemes.

Just activate the option "Output of debug information on the JS console" in order to figure out the correct values.
