We can use Visual Editor to edit templates, and in the process better separate code, content, and presentation. I'm going to call these "Visual Templates".
Here are the key ideas https://wikimania2015.wikimedia.org/w/index.php?title=File%3ATemplates_are_dead!_Long_live_templates!.pdf&page=35 :
1. A minimal "logic-less" template language, like the Spacebars https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md variant of Handlebars http://handlebarsjs.com//mustache https://mustache.github.io/ templates. There are only three basic template constructs (variable interpolation, block tags, and partials), which can all be visualized in the editor. There are no hidden directives. 2. All logic is written in a real programming language, via Scribunto https://www.mediawiki.org/wiki/Extension:Scribunto (or something very much like it). In the examples below I'll be using a version of Scribunto that supports https://github.com/preillyme/v8js JavaScript 2015 as well as Lua; forgive my eccentricities. 3. All data is escaped and hygienic by default. String data is automatically escaped. The Scribunto code manipulates and returns DocumentFragments https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment, not wikitext strings. 4. Similarly, arguments are provided as JSON types (strings/numerical data), DocumentFragments, or wikidata queries, not wikitext. 5. Composition of content is structural, not via string concatenation. You can't "accidentally" create a list item if your template result happens to start with a : or *.
So: every Visual Template is composed of two parts: a Scribunto module (edited with a code editor), and a "layout" (edited with Visual Editor).
Here's what the layout of a particular template might look like:
https://phabricator.wikimedia.org/F2651938 Forgive the terrible yellow; a designer could make this look much nicer. Note that there are two "block helper" invocations, `each` and `caption`. The `each` helper is a built-in, and it is given `row` from the template data context object. The `caption` helper is defined by the Scribunto module. Each row of the table gets "variable" invocations to fill in the table cells.
There are only three new functions added to the VE toolbar in template mode, for inserting "variable", "block helper", and "partial" elements. Context-sensitive help will be given to choose from available variable, block helper, and partial names.
The invocation of this template would, for this example, take structured data of the following form, perhaps from a wikidata query:
| event | gold | silver | bronze | | Shotput | USA | GER | MEX | | Discus | MEX | USA | GER | | Javelin | GER | MEX | USA |
The Scribunto module transforms this raw data by computing medal counts for each country. Here's what it might look like, using JavaScript 2015 (ES6) https://esdiscuss.org/topic/javascript-2015 module syntax, ``` // The main module entry point is a transformation of a // "data context". // In this example the data context comes from a wikidata query. export default function(events) { // `events` is an array: each item has `event`, `gold`, // `silver`, and `bronze` props (the latter three holding // country names) let totals = new Map(), countries = new Set(), allColors = ['gold','silver','bronze']; function mget(m, key, defaultval) { if (!m.has(key)) { m.set(key, defaultval); } return m.get(key); } function inc(country, color) { let old = mget(mget(totals, country, new Map()), color, 0); totals.get(country).set(color, old + 1); countries.add(country); } // count up how many medals of each color a given country has. events.forEach( (e) => { for (let color of allColors) { inc(e[color], color); } }): // Now sort the countries by medal count let rows = Array.from(countries).sort( (a,b) => { let aa = totals.get(a), bb = totals.get(b); for (let color of allColors) { let c = aa[color] - bb[color]; if (c!==0) { return c; } } return c; }).map( (c) => totals.get(c) ); // our resulting data context object has just one // property, named 'rows', which is an array. return { row: rows }; } // Additional block helper. Helpers must return a // `DocumentFragment`, though for brevity we allow `Node` or // `Array<Node>` as well. In fact, if you returned a string // we'd make a text node for you, but that would make this // example even more trivial. export function caption() { return document.createTextNode('caption'); } ```
The default entry point transforms the template arguments to set up the template data context. //*It does no text manipulation.*// There is a second `caption` entry point, which defines the `caption` block helper. The exact API used in block helpers definitions might be tweaked: one obvious improvement would be to allow jquery methods for DOM construction, instead of using the DOM API directly. The key point is that the result is conceptually a chunk of structural DOM, not a wikitext string.
***Visual Template invocation in Visual Editor***
The primary authoring mechanism for Visual Templates is expected to be Visual Editor. The UX is expected to be roughly the same as exists now for templates: a mechanism like TemplateData exposes the expected types of arguments and descriptive text, so that Visual Editor can expose appropriate editing widgets. Initially the expected types are: "markup" (edited with VE recursively), JSON (edited with a JSON editor, as with TemplateData), and "wikidata" (a SPARQL wikidata query https://www.mediawiki.org/wiki/Wikidata_query_service/User_Manual).
***Visual Template Invocation in wikitext***
This is a strawman for the wikitext serialization of a template invocation: ``` {{#visual template_name param1=<markup>'''bold'''</markup> param2=<json>{"foo":"bar"}</json> param3=<wikidata> SELECT ?spaceProbeLabel ?date ?picture WHERE { ?spaceProbe wdt:P31 wd:Q26529 ; wdt:P18 ?picture ; wdt:P619 ?date . SERVICE wikibase:label { bd:serviceParam wikibase:language "fr,en" . } } ORDER BY ?date </wikidata> }} ``` The parameter name `entity` is reserved; the data context object for the template always includes the wikidata entity object (equivalent to ` mw.wikibase.getEntityObject()`) as an implicit parameter named `entity`. This makes it easy to generate infoboxes (for example), since you can just insert `{{#formatproperty entity.P17}}` in your template (equivalent to `entity.formatPropertyValues( 'P17' ).value`). Hopefully there will be a friendly shortcut for ` entity.<something>`, which will include mapping wikidata identifier ` <something>` to a label in your language.
Since arguments are delimited by html-style tags, the usual & and < escapes are all that are required to include arbitrary data inside a parameter, and whitespace can be used to separate arguments. The parameter names can be omitted for positional arguments.
//*Perhaps we can allow for an abbreivated format for certain arguments: `"..."` -> literal string, `\d+` -> number, `{...}` -> JSON value. Then unit conversion (for example) can still be simply `{{#visual convertToMiles 5 km}}`*//
****Syntax details can be tweaked: your input wanted!****
Phabricator ticket: https://phabricator.wikimedia.org/T114454 --scott
wikitech-l@lists.wikimedia.org