xref: /plugin/usersettings/README.md (revision d00c9837cc1955f190d39d9e5c8adb79eb3c011d)
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![User Preferences page with toggles and language selector](images/usersettings-screen1.png)
13![Admin overview table showing all users' effective preferences](images/usersettings-screen2.png)
14![Per-user edit form for changing another user's preferences](images/usersettings-screen3.png)
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