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