User:Salvatore Ingala/Notes

= Intro = Scope of the project is to make gadgets customizable, that is:
 * Allow gadget writers to easily export their gadget's customization variables.
 * Provide the users with a nice UI for customizing those variables.

This page contains a newer design than the one I proposed in my application, since the scope of the project has slightly changed.

Places
I'm working on this branch: http://svn.wikimedia.org/viewvc/mediawiki/branches/salvatoreingala/Gadgets/ Previous brainstorming was done on EtherPad. This is the last useful revision.

= Features definition = These are the needed features:
 * 1) Ability to specify what preferences do we have for a gadget;
 * 2) *Will do this in JSON format.
 * 3) *Where? For now, I'm using a fixed page MediaWiki:Gadget-mygadget.preferences or something similar. Likely to change (with low impact)
 * 4) *STATUS: implemented (with a limited number of preference type, documented below; more will be added); server-side validation done (needs refinements); client site validation done with jquery.validate (link).
 * 5) Hooking up to Special:Preferences to add per-gadget prefs
 * 6) *Currently done like this: every enabled gadget shows a "configure" link near his checkbox in Special:Preferences. Clicking on the link pops up a modal dialog with the configuration interface. Saving preferences for that specific gadget could be done when closing the dialog. This should be robust against future changes of Special:Preferences.
 * 7) *STATUS: working UI mock-up
 * 8) Store preferences somewhere
 * 9) *Using the existing user_properties table. Gadget preferences are retrieved and then hidden in UserLoadOptions hook, and reinserted in UserSaveOptions.
 * 10) *Unused preferences need to be deleted (lazily is good enough). Saving policy to maintain coherency:
 * 11) **When saving the configuration of a gadget, throw away any previous configuration for that gadget (so to clean up old values for no-longer-existing settings).
 * 12) **When reading the configuration of a gadget, check it against its specification and assume default for missing values or values that fail validation (if specifications changed).
 * 13) *STATUS: implemented
 * 14) Provide the preferences to gadgets. Concepts:
 * 15) *mw.gadgets.options.get( 'gadgetname' )?
 * 16) *mw.loader.options.get( 'modulename' )?
 * 17) *binding the configuration to this in gadget's context. (may have issues with RL2, unreliable for now)
 * 18) *STATUS: implemented, currently binding the configuration to this, which is my preferred solution :P - It's easy to switch to any other version.
 * 19) Internationalization of strings used by the gadget
 * 20) *There are two types of "messages" to handle:
 * 21) *#messages shown in the configuration dialog; these don't need to be passed to the gadget's module, and may be interpreted server-side, when building the dialog code during the AJAX request. Should fetch their names form the configuration JSON (maybe adding some common prefix);
 * 22) *#other messages used by the gadget; this is in the scope of RL2.
 * 23) *STATUS: implemented (with some issues to fix)

= Preference description syntax =

The "preference description" is an object in JSON format which describes all relevant info about gadget's settings (and some other settings related to the configuration system); when all the rest of the project will be finished, a tool to create and maintain preference descriptions may be built, so to relieve gadget developers from the annoyance of learning its syntax.

The general format is this:

"global" settings will be settings that apply to the preference dialog (e.g.: min/max width, min/max height, title...). It will be useful for future advanced features, too.

is an object containing gadget's settings along with their option description objects.

The syntax of a single option description object is as follows:

,, and   are compulsory for any option description. According to, more fields may be allowed (or needed).

must be a valid JavaScript (that is, it must start with a letter or '_', then contain only letters, numbers or '_') identifier, and must not be more than 40 characters long.

Raw strings and messages
The ability to specify messages in the "MediaWiki:" namespace is absolutely necessary; nevertheless, sometimes raw strings are needed. Instead of making complex syntaxes to distinguish between raw strings and messages, a preprocessing of strings is done: if the string starts with "@", then it's intended as a message prefixed by "MediaWiki:Gadget-mygadget" (where  is the name of the gadget, case-sensitive); otherwise, it's a raw string. Two "@" at the beginning escape for a single "@". This currently applies to  fields and to options of   fields.

intro
A string or message placed on top of the dialog's content.

Option definition specifications
This section describes additional fields allowed for each option.

boolean
This type will be shown as a checkbox. No additional fields are allowed.

string</tt>
This will be shown as a single-line text field. There are additional fields:


 * required</tt>: (optional boolean, defaults to false</tt>). If true</tt>, a zero-length string will never be accepted; if false</tt>, a zero-length string will always be accepted (even when minlength</tt> is given).
 * minlength</tt> (optional integer, defaults to 0): an integer number specifying the minimum length allowed for this option.
 * maxlength</tt> (optional integer, defaults to a big integer): an integer number specifying the minimum length allowed for this option.

number</tt>
This will be shown as a single-line text field. The javascript representation of values of this field is as numeric datatypes (or null). There are additional fields:


 * required</tt>: (optional boolean, defaults to true</tt>). If true</tt>, a valid number must be given. If false</tt>, an empty string will be accepted (and will be delivered as null</tt>).
 * min</tt> (optional number): a number specifying the minimum allowed value.
 * max</tt> (optional number): a number specifying the maximum allowed value.
 * <tt>integer</tt> (optional boolean, defaults to <tt>false</tt>). If <tt>true</tt>, only integer numbers will be accepted (<tt>min</tt> and <tt>max</tt> must honor this, too). If <tt>false</tt>, decimal number will be allowed, too.

<tt>select</tt>
This will be shown as an HTML <tt>select</tt> element. There is only an additional compulsory field:


 * <tt>options</tt>: (compulsory object): an object with fields of the type <tt>"msg": value</tt>, where <tt>msg</tt> behaves like labels, and <tt>value</tt> is an arbitrary javascript value (allowed values are <tt>null</tt>, booleans, numbers or strings. Different option values may have different types. All values should be different (according to the <tt>===</tt> operator). No two <tt>"msg"</tt>s may be equal, of course.

<tt>range</tt>
Shown as a jQuery.UI Slider, even if only its basic functionality is currently supported. There are other fields:


 * <tt>min</tt>: (compulsory number): Minimum allowed number.
 * <tt>max</tt>: (compulsory number): Maximum allowed number.
 * <tt>step</tt>: (optional number, defaults to 1): Gap between allowed numbers.

Negative or decimal numbers are valid values for <tt>min</tt>, <tt>max</tt> and <tt>step</tt>.

Note that <tt>max</tt> must be coherent with <tt>min</tt> and <tt>step</tt> (that is, the difference between <tt>min</tt> and <tt>max</tt> must be an integer multiple of <tt>step</tt>).

<tt>date</tt>
Shown as a jQuery.UI [http://jqueryui.com/demos/datepicker/ datepicker. There are no other fields.

Dates are delivered as string with the format: <tt>[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]Z</tt>, which is an UTC time in ISO 8601 standard. It is a format recognized by Javascript's <tt>Date</tt> constructor. Empty dates are delivered as <tt>null</tt>.


 * TODO: add a <tt>required</tt> option.

<tt>color</tt>
Shows a color picker (farbtastic); colors are delivered as hexadecimal strings from "#000000" to "#ffffff" (uppercase letters are not allowed)


 * TODO: add a <tt>required</tt> option; add an option to allow choosing 'transparent' as a color?

Easy to implement

 * multiselects
 * 'url' fields

Ideas for further enhancements
What follows is a collection of unimplemented (and, possibly, not well defined yet) ideas.

Groups
Gadget with lots of preferences may want to subdivide gadget preferences in several named sections. Groups of sections may be shown as fieldsets, accordion, tabs or other fancy widgets. Each section will then have each own set of fields.

Proposed syntax:

Section description behaves much alike a complete preferences description, with the additional limitation that the set of preferences of all section must be disjoint (obviously).

The name of a group is not a gadget preference, but must be different from all gadget preferences, nevertheless.

Sections may have groups as fields (yeah, recursive syntax!); just don't overdo it ;).

Field dependencies
Sometimes one wants to enable a field (or a set of fields) only if some other fields satisfy some contraint. Most typical case is a checkbox which, if checked, enables one or more fields.

Proposed syntax for simple conditions:

The field described by <tt>someField</tt> (in this case a string field) will be enabled only if the value of the field described by <tt>fieldName</tt> is <tt>true</tt> (or something that evaluates to <tt>true</tt>, like non-empty strings or non-zero numbers).

A syntax for more advanced conditions may be added later, after evaluating if doing that is worth the added code complexity (it would not be difficult to allow arbitrary boolean predicates on other fields).

Extensibility
No matter how many features will be added to the preference description syntax, there will always be some specific use-case that still needs something that is not provided. Instead of adding tons of new features to cover each and every use-case, it may be better to allow gadget writers to enhance preference dialogs with their own code, which interacts with the dialog with some well defined interface.

This conflicts with an established (and well motivated) policy of not allowing any user defined code to be run on Special:Preferences (even if the "user" is the gadget's writer, which is usually more trusted than ordinary users). A way to solve this is putting user-provided code in an a ResourceLoader module which is registered on Special:Preferences, but not delivered; then, only if the user clicks the "Configure" link for that gadget, the module is downloaded and executed. In this way, the end user would be able to disable the gadget on Special:Preferences, without the risk of malicious code tampering with the interface.