xref: /plugin/usersettings/README.md (revision 54f11439a68c5f07eddd4c7f20a09acd149bde82)
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