1# User Settings plugin for DokuWiki 2 3A self-contained, server-side store of per-user preference toggles, with a 4self-service settings page for every logged-in user and an admin overview of 5everyone's choices. 6 7It is **infrastructure for other plugins**: a feature plugin (or a template's 8companion plugin) registers its own toggle with a single event handler, and 9this plugin renders it, stores it, and exposes it — without ever needing to be 10edited itself. 11 12## Why this plugin exists 13 14DokuWiki core has no server-side per-user preference store. The auth backend 15holds only name, e-mail, password and groups, and DokuWiki's built-in 16interface preferences live in a browser **cookie** — so they do not follow a 17user from one browser or device to another. 18 19This plugin fills that gap. Preferences are stored on the server, keyed by 20user, so they apply wherever the person is logged in. 21 22## What a user sees 23 24A **"Preferences"** item appears in the user menu, just to the left of 25"Update Profile". It opens a settings page (`do=usersettings`) listing every 26registered toggle as a checkbox or a drop-down. The page is a plain HTML form — 27no JavaScript — so it works in any browser. 28 29## What an admin sees 30 31Admin → **User Settings** shows a flat, sortable table — one row per 32(user × setting): *Username · Display name · Email · Groups · Setting · Value · 33Changed by · Changed at*. The Email and Groups columns can each be hidden from 34the configuration. A row shows the user's explicit choice, or the toggle's 35default (marked as such) when they never set one. 36 37Every column except *Changed at* is searchable: a per-column text-filter row 38(case-insensitive substring, modelled on the User Manager's search row) 39narrows the table, while the *Setting* column keeps its drop-down for picking a 40single setting. The table is paginated with numbered page links and a 41configurable page size. Filtering, sorting and the current page all travel in 42the URL, so the view is bookmarkable and needs no JavaScript. 43 44Clicking a display name opens an edit form for that user. This is **Model A+**: 45an admin may change anyone's preferences, and the change is recorded under the 46admin's name — but it is *not* enforced. The user can always change it back. 47This is how you roll out a new feature as default-on while pre-setting it off 48for specific people (for example, keeping two conservative admins on the old 49theme). 50 51## Storage 52 53One JSON file per user, under `{metadir}/usersettings/`, holding 54`{key: {value, changed_at, changed_by}}`. JSON and pretty-printed, so the files 55are easy to inspect or back up. The page text and the wiki changelog are never 56touched. `changed_by` records whoever made the change — the user, or an admin 57acting on their behalf — which is what the overview's "Changed by" column 58shows. 59 60## Registering a toggle from another plugin 61 62This is the integration point. Your plugin hooks the 63`PLUGIN_USERSETTINGS_REGISTER` event and appends one or more toggle 64definitions: 65 66```php 67class action_plugin_myfeature extends DokuWiki_Action_Plugin 68{ 69 public function register(Doku_Event_Handler $controller) 70 { 71 $controller->register_hook( 72 'PLUGIN_USERSETTINGS_REGISTER', 'BEFORE', $this, 'registerToggles' 73 ); 74 } 75 76 public function registerToggles(Doku_Event $event) 77 { 78 // a simple on/off toggle 79 $event->data[] = [ 80 'key' => 'myfeature_enabled', 81 'label' => 'Enable my feature', 82 'type' => 'checkbox', 83 'default' => 1, 84 'desc' => 'Show my feature on wiki pages.', 85 'plugin' => 'myfeature', 86 ]; 87 88 // a choice from a fixed list 89 $event->data[] = [ 90 'key' => 'myfeature_mode', 91 'label' => 'My feature mode', 92 'type' => 'select', 93 'options' => ['compact' => 'Compact', 'full' => 'Full view'], 94 'default' => 'full', 95 'plugin' => 'myfeature', 96 ]; 97 } 98} 99``` 100 101### Toggle definition fields 102 103| Field | Required | Notes | 104| --- | --- | --- | 105| `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. | 106| `label` | yes | Shown on the settings page and in the admin table. | 107| `type` | — | `checkbox` (default) or `select`. | 108| `default` | — | Default value. Checkbox: `0`/`1`. Select: one of the option keys. | 109| `options` | for `select` | A `value => label` map. Required and non-empty for selects. | 110| `desc` | — | Optional help text shown under the toggle. | 111| `plugin` | — | Optional identifier of the registering plugin/template. | 112 113Invalid definitions (missing key, illegal key characters, a select with no 114options) are silently dropped. If two plugins register the same `key`, the 115first registration wins. 116 117A toggle definition is plain data, so a **template** can have a toggle too — 118ship a tiny companion action plugin that does nothing but register it. 119 120## Built-in toggles 121 122### Interface language 123 124This plugin ships with a built-in **Interface language** toggle that allows 125each logged-in user to select their preferred language for DokuWiki's menus 126and messages, overriding the site-wide default (`$conf['lang']`). 127 128- **Key:** `lang` 129- **Type:** `select` 130- **Options:** All installed languages found in `inc/lang/`, displayed by their 131 native name (endonym) — e.g. *Deutsch*, *日本語*, *Français*. Unknown codes 132 fall back to the bare code. 133- **Default:** The site's configured default language 134 135The language preference is applied as early as possible in the request 136lifecycle (during `ACTION_ACT_PREPROCESS`), so all rendering — template hooks, 137plugins, the wiki text itself — sees the user's chosen language immediately. 138 139Users can change it in the Preferences page. Admins can set it per-user in the 140User Settings admin table. The language list is scanned from `inc/lang/` at 141registration time, which fires only once per request (when the settings page or 142admin table is actually visited), not on every page load. 143 144## Reading a preference 145 146Your plugin reads the effective value through the helper. `getPreference()` 147returns the user's stored value, or the registered default if they never set 148one: 149 150```php 151$prefs = plugin_load('helper', 'usersettings'); 152$enabled = $prefs ? $prefs->getPreference('myfeature_enabled', null) : 1; 153// pass a username as the 2nd argument, or null for the current user 154``` 155 156Always provide your own sensible fallback (`? ... : 1` above) in case the 157User Settings plugin is not installed. 158 159## Configuration 160 161Admin → Configuration Settings. These affect only the admin overview table; 162the self-service Preferences page is unchanged. 163 164| Setting | Default | Effect | 165| --- | --- | --- | 166| `show_mail` | `1` (on) | Show the Email column in the admin overview. | 167| `show_grps` | `1` (on) | Show the Groups column in the admin overview. | 168| `entries_per_page` | `20` | Rows per page in the admin overview. Set to `0` to show all rows on one page (no pagination). | 169 170## Components 171 172| File | Role | 173| --- | --- | 174| `helper.php` | Storage, the registration event, the read/write API. | 175| `action.php` | The user-menu item, the `do=usersettings` settings page, and the built-in interface language toggle. | 176| `admin.php` | The admin overview table and per-user edit form. | 177| `MenuItem.php` | The user-menu item class. | 178 179## Install 180 181Drop the folder into `lib/plugins/usersettings/`, or use Admin → Extension 182Manager → Manual Install. The admin-overview columns and page size can be 183tuned under **Configuration** (above), but the defaults are sensible. 184 185## Translations 186 187Included: English (`en`), German (`de`), Russian (`ru`), Japanese (`ja`). 188 189## Notes 190 191- The settings page and admin pages are plain HTML forms — no JavaScript — so 192 there are no old-browser concerns. 193- The **built-in interface language toggle** is registered automatically by 194 `action.php` and requires no configuration. It scans `inc/lang/` at 195 registration time to populate the option list, so all installed languages 196 are immediately available to users. The toggle applies the selected language 197 during `ACTION_ACT_PREPROCESS`, before any output is produced, ensuring 198 consistency throughout the request. 199- This is a new, locally-developed plugin with no upstream, so — unlike the 200 forked plugins on this wiki — it carries no update-suppression date. 201 202## License 203 204GPL 2, matching DokuWiki. 205