11ab40613Stracker-user# User Settings plugin for DokuWiki 21ab40613Stracker-user 31ab40613Stracker-userA self-contained, server-side store of per-user preference toggles, with a 41ab40613Stracker-userself-service settings page for every logged-in user and an admin overview of 51ab40613Stracker-usereveryone's choices. 61ab40613Stracker-user 71ab40613Stracker-userIt is **infrastructure for other plugins**: a feature plugin (or a template's 81ab40613Stracker-usercompanion plugin) registers its own toggle with a single event handler, and 91ab40613Stracker-userthis plugin renders it, stores it, and exposes it — without ever needing to be 101ab40613Stracker-useredited itself. 111ab40613Stracker-user 12*d00c9837Stracker-user 13*d00c9837Stracker-user 14*d00c9837Stracker-user 15*d00c9837Stracker-user 161ab40613Stracker-user## Why this plugin exists 171ab40613Stracker-user 181ab40613Stracker-userDokuWiki core has no server-side per-user preference store. The auth backend 191ab40613Stracker-userholds only name, e-mail, password and groups, and DokuWiki's built-in 201ab40613Stracker-userinterface preferences live in a browser **cookie** — so they do not follow a 211ab40613Stracker-useruser from one browser or device to another. 221ab40613Stracker-user 231ab40613Stracker-userThis plugin fills that gap. Preferences are stored on the server, keyed by 241ab40613Stracker-useruser, so they apply wherever the person is logged in. 251ab40613Stracker-user 261ab40613Stracker-user## What a user sees 271ab40613Stracker-user 281ab40613Stracker-userA **"Preferences"** item appears in the user menu, just to the left of 291ab40613Stracker-user"Update Profile". It opens a settings page (`do=usersettings`) listing every 301ab40613Stracker-userregistered toggle as a checkbox or a drop-down. The page is a plain HTML form — 311ab40613Stracker-userno JavaScript — so it works in any browser. 321ab40613Stracker-user 331ab40613Stracker-user## What an admin sees 341ab40613Stracker-user 351ab40613Stracker-userAdmin → **User Settings** shows a flat, sortable table — one row per 3654f11439Stracker-user(user × setting): *Username · Display name · Email · Groups · Setting · Value · 3754f11439Stracker-userChanged by · Changed at*. The Email and Groups columns can each be hidden from 3854f11439Stracker-userthe configuration. A row shows the user's explicit choice, or the toggle's 3954f11439Stracker-userdefault (marked as such) when they never set one. 4054f11439Stracker-user 4154f11439Stracker-userEvery column except *Changed at* is searchable: a per-column text-filter row 4254f11439Stracker-user(case-insensitive substring, modelled on the User Manager's search row) 4354f11439Stracker-usernarrows the table, while the *Setting* column keeps its drop-down for picking a 4454f11439Stracker-usersingle setting. The table is paginated with numbered page links and a 4554f11439Stracker-userconfigurable page size. Filtering, sorting and the current page all travel in 4654f11439Stracker-userthe URL, so the view is bookmarkable and needs no JavaScript. 471ab40613Stracker-user 481ab40613Stracker-userClicking a display name opens an edit form for that user. This is **Model A+**: 491ab40613Stracker-useran admin may change anyone's preferences, and the change is recorded under the 501ab40613Stracker-useradmin's name — but it is *not* enforced. The user can always change it back. 511ab40613Stracker-userThis is how you roll out a new feature as default-on while pre-setting it off 521ab40613Stracker-userfor specific people (for example, keeping two conservative admins on the old 531ab40613Stracker-usertheme). 541ab40613Stracker-user 551ab40613Stracker-user## Storage 561ab40613Stracker-user 571ab40613Stracker-userOne JSON file per user, under `{metadir}/usersettings/`, holding 581ab40613Stracker-user`{key: {value, changed_at, changed_by}}`. JSON and pretty-printed, so the files 591ab40613Stracker-userare easy to inspect or back up. The page text and the wiki changelog are never 601ab40613Stracker-usertouched. `changed_by` records whoever made the change — the user, or an admin 611ab40613Stracker-useracting on their behalf — which is what the overview's "Changed by" column 621ab40613Stracker-usershows. 631ab40613Stracker-user 641ab40613Stracker-user## Registering a toggle from another plugin 651ab40613Stracker-user 661ab40613Stracker-userThis is the integration point. Your plugin hooks the 671ab40613Stracker-user`PLUGIN_USERSETTINGS_REGISTER` event and appends one or more toggle 681ab40613Stracker-userdefinitions: 691ab40613Stracker-user 701ab40613Stracker-user```php 711ab40613Stracker-userclass action_plugin_myfeature extends DokuWiki_Action_Plugin 721ab40613Stracker-user{ 731ab40613Stracker-user public function register(Doku_Event_Handler $controller) 741ab40613Stracker-user { 751ab40613Stracker-user $controller->register_hook( 761ab40613Stracker-user 'PLUGIN_USERSETTINGS_REGISTER', 'BEFORE', $this, 'registerToggles' 771ab40613Stracker-user ); 781ab40613Stracker-user } 791ab40613Stracker-user 801ab40613Stracker-user public function registerToggles(Doku_Event $event) 811ab40613Stracker-user { 821ab40613Stracker-user // a simple on/off toggle 831ab40613Stracker-user $event->data[] = [ 841ab40613Stracker-user 'key' => 'myfeature_enabled', 851ab40613Stracker-user 'label' => 'Enable my feature', 861ab40613Stracker-user 'type' => 'checkbox', 871ab40613Stracker-user 'default' => 1, 881ab40613Stracker-user 'desc' => 'Show my feature on wiki pages.', 891ab40613Stracker-user 'plugin' => 'myfeature', 901ab40613Stracker-user ]; 911ab40613Stracker-user 921ab40613Stracker-user // a choice from a fixed list 931ab40613Stracker-user $event->data[] = [ 941ab40613Stracker-user 'key' => 'myfeature_mode', 951ab40613Stracker-user 'label' => 'My feature mode', 961ab40613Stracker-user 'type' => 'select', 971ab40613Stracker-user 'options' => ['compact' => 'Compact', 'full' => 'Full view'], 981ab40613Stracker-user 'default' => 'full', 991ab40613Stracker-user 'plugin' => 'myfeature', 1001ab40613Stracker-user ]; 1011ab40613Stracker-user } 1021ab40613Stracker-user} 1031ab40613Stracker-user``` 1041ab40613Stracker-user 1051ab40613Stracker-user### Toggle definition fields 1061ab40613Stracker-user 1071ab40613Stracker-user| Field | Required | Notes | 1081ab40613Stracker-user| --- | --- | --- | 1091ab40613Stracker-user| `key` | yes | Unique identifier; `A-Z a-z 0-9 _` only. Also the storage key and HTML field name. Prefix it with your plugin name to avoid collisions. | 1101ab40613Stracker-user| `label` | yes | Shown on the settings page and in the admin table. | 1111ab40613Stracker-user| `type` | — | `checkbox` (default) or `select`. | 1121ab40613Stracker-user| `default` | — | Default value. Checkbox: `0`/`1`. Select: one of the option keys. | 1131ab40613Stracker-user| `options` | for `select` | A `value => label` map. Required and non-empty for selects. | 1141ab40613Stracker-user| `desc` | — | Optional help text shown under the toggle. | 1151ab40613Stracker-user| `plugin` | — | Optional identifier of the registering plugin/template. | 1161ab40613Stracker-user 1171ab40613Stracker-userInvalid definitions (missing key, illegal key characters, a select with no 1181ab40613Stracker-useroptions) are silently dropped. If two plugins register the same `key`, the 1191ab40613Stracker-userfirst registration wins. 1201ab40613Stracker-user 1211ab40613Stracker-userA toggle definition is plain data, so a **template** can have a toggle too — 1221ab40613Stracker-usership a tiny companion action plugin that does nothing but register it. 1231ab40613Stracker-user 124cc98f4d1Stracker-user## Built-in toggles 125cc98f4d1Stracker-user 126cc98f4d1Stracker-user### Interface language 127cc98f4d1Stracker-user 128cc98f4d1Stracker-userThis plugin ships with a built-in **Interface language** toggle that allows 129cc98f4d1Stracker-usereach logged-in user to select their preferred language for DokuWiki's menus 130cc98f4d1Stracker-userand messages, overriding the site-wide default (`$conf['lang']`). 131cc98f4d1Stracker-user 132cc98f4d1Stracker-user- **Key:** `lang` 133cc98f4d1Stracker-user- **Type:** `select` 13449b74e0aStracker-user- **Options:** All installed languages found in `inc/lang/`, displayed by their 13549b74e0aStracker-user native name (endonym) — e.g. *Deutsch*, *日本語*, *Français*. Unknown codes 13649b74e0aStracker-user fall back to the bare code. 137cc98f4d1Stracker-user- **Default:** The site's configured default language 138cc98f4d1Stracker-user 139cc98f4d1Stracker-userThe language preference is applied as early as possible in the request 140cc98f4d1Stracker-userlifecycle (during `ACTION_ACT_PREPROCESS`), so all rendering — template hooks, 141cc98f4d1Stracker-userplugins, the wiki text itself — sees the user's chosen language immediately. 142cc98f4d1Stracker-user 143cc98f4d1Stracker-userUsers can change it in the Preferences page. Admins can set it per-user in the 14449b74e0aStracker-userUser Settings admin table. The language list is scanned from `inc/lang/` at 14549b74e0aStracker-userregistration time, which fires only once per request (when the settings page or 14649b74e0aStracker-useradmin table is actually visited), not on every page load. 147cc98f4d1Stracker-user 1481ab40613Stracker-user## Reading a preference 1491ab40613Stracker-user 1501ab40613Stracker-userYour plugin reads the effective value through the helper. `getPreference()` 1511ab40613Stracker-userreturns the user's stored value, or the registered default if they never set 1521ab40613Stracker-userone: 1531ab40613Stracker-user 1541ab40613Stracker-user```php 1551ab40613Stracker-user$prefs = plugin_load('helper', 'usersettings'); 1561ab40613Stracker-user$enabled = $prefs ? $prefs->getPreference('myfeature_enabled', null) : 1; 1571ab40613Stracker-user// pass a username as the 2nd argument, or null for the current user 1581ab40613Stracker-user``` 1591ab40613Stracker-user 1601ab40613Stracker-userAlways provide your own sensible fallback (`? ... : 1` above) in case the 1611ab40613Stracker-userUser Settings plugin is not installed. 1621ab40613Stracker-user 16354f11439Stracker-user## Configuration 16454f11439Stracker-user 16554f11439Stracker-userAdmin → Configuration Settings. These affect only the admin overview table; 16654f11439Stracker-userthe self-service Preferences page is unchanged. 16754f11439Stracker-user 16854f11439Stracker-user| Setting | Default | Effect | 16954f11439Stracker-user| --- | --- | --- | 17054f11439Stracker-user| `show_mail` | `1` (on) | Show the Email column in the admin overview. | 17154f11439Stracker-user| `show_grps` | `1` (on) | Show the Groups column in the admin overview. | 17254f11439Stracker-user| `entries_per_page` | `20` | Rows per page in the admin overview. Set to `0` to show all rows on one page (no pagination). | 17354f11439Stracker-user 1741ab40613Stracker-user## Components 1751ab40613Stracker-user 1761ab40613Stracker-user| File | Role | 1771ab40613Stracker-user| --- | --- | 1781ab40613Stracker-user| `helper.php` | Storage, the registration event, the read/write API. | 179cc98f4d1Stracker-user| `action.php` | The user-menu item, the `do=usersettings` settings page, and the built-in interface language toggle. | 1801ab40613Stracker-user| `admin.php` | The admin overview table and per-user edit form. | 1811ab40613Stracker-user| `MenuItem.php` | The user-menu item class. | 1821ab40613Stracker-user 1831ab40613Stracker-user## Install 1841ab40613Stracker-user 1851ab40613Stracker-userDrop the folder into `lib/plugins/usersettings/`, or use Admin → Extension 18654f11439Stracker-userManager → Manual Install. The admin-overview columns and page size can be 18754f11439Stracker-usertuned under **Configuration** (above), but the defaults are sensible. 1881ab40613Stracker-user 189f51fe07cStracker-user## Translations 190f51fe07cStracker-user 191f51fe07cStracker-userIncluded: English (`en`), German (`de`), Russian (`ru`), Japanese (`ja`). 192f51fe07cStracker-user 1931ab40613Stracker-user## Notes 1941ab40613Stracker-user 1951ab40613Stracker-user- The settings page and admin pages are plain HTML forms — no JavaScript — so 1961ab40613Stracker-user there are no old-browser concerns. 197cc98f4d1Stracker-user- The **built-in interface language toggle** is registered automatically by 198cc98f4d1Stracker-user `action.php` and requires no configuration. It scans `inc/lang/` at 199cc98f4d1Stracker-user registration time to populate the option list, so all installed languages 200cc98f4d1Stracker-user are immediately available to users. The toggle applies the selected language 201cc98f4d1Stracker-user during `ACTION_ACT_PREPROCESS`, before any output is produced, ensuring 202cc98f4d1Stracker-user consistency throughout the request. 2031ab40613Stracker-user- This is a new, locally-developed plugin with no upstream, so — unlike the 2041ab40613Stracker-user forked plugins on this wiki — it carries no update-suppression date. 2051ab40613Stracker-user 2061ab40613Stracker-user## License 2071ab40613Stracker-user 2081ab40613Stracker-userGPL 2, matching DokuWiki. 209