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 &amp; and &lt;
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
-- 
(http://cscott.net)
_______________________________________________
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

Reply via email to