User shortcut preferences implementation
Description
Design
For the position of the shortcuts, we discussed it on this forum topic: https://forum.xwiki.org/t/shortcut-settings-position/14116
For the presentation, I went and reused the standard form field design:
`
Implementation
Interaction with the current shortcut model
The current shortcut model was pretty simple: each shortcut is bound to one translationKey. The value of this translation Key would impact the keys needed to be pressed to activate the shortcut. This means that the default shortcuts are stored in a translation file, and that admin users could easily overwrite these values at different levels to change the shortcuts on their wikis.
This new system proposes to add user based preferences. Each creation of a shortcut in javascript with shortcut.add now calls a `getShortcutValue` macro instead of getting directly the translation.
This velocity macro takes one argument, the translation key. The conversion from the old model to the new one is quite straightforward. This does not break any shortcut set-up the old way (with a translation call).
The `getShortcutValue` velocity macro then queries if the current user has a preference about this specific shortcut. If the user does, it's returned directly. If not, it falls back on the wiki defined translation. Note that by making things this way, it makes it impossible for the admin user to force the value of a shortcut (except by removing the preference editor directly and/or removing the preferences set by users with an extra script).
Right now, the shortcut model supports multiple types of shortcuts. For this first implementation, only simple shortcuts (one letter, no modifier) are considered. More complex shortcut patterns can be considered as future improvement (especially if requested by users).
Storing preferences
Instead of adding additional properties to the user object, I decided to store shortcut preferences in a different object. Those `XWiki.UserPreferenceShortcutsClass` objects are contained on each user page.
Each UserPreferenceShortcut has two fields:
- a translationKey that contains the translation key used to store the shortcut (this translationkey model for shortcut was implemented before and IMO it's okay to keep working with it)
- a value that contains the value of the shortcut defined by the user
In order to make it work properly, we expect the shortcut preference to be on the page of the user it's defined for. Moving them around would break them.
UserPreferenceShortcuts are handled live when the user edits their profile preferences. It does not wait for the user to submit the page to update them. Technically, the script waits half a second after the last edit from the user to save the new value.
If a preference is set to be empty, the shortcut will be unbound. If a preference is set to be the same value as the default value (the one from the translation), the UserPreferenceShortcuts is removed. There is only one UserPreferenceShortcuts per user per translationKey. There might be as much UserPreferenceShortcuts as there are shortcuts for each user. By default, there is no UserPreferenceShortcuts on a user profile.
This model allows to keep the amount of stored data to a minimum, there are no field stored that do not contain any information.
Dynamic preference displays
I decided to filter out the access to shortcut preferences based on the type of user. Basic users can only edit the shortcuts for the editor actions and the dev shortcuts. Advanced users also get to edit all of their preference for the edit mode shortcuts, the view modes and special actions.
This was made in order to declutter the UI for basic users. IMO those users would rarely access secondary edit modes, view modes (with shortcuts at least) or delete a page with a shortcut.
Lucas Charpentier