Extension:CollaborationKit/Technical documentation

CollaborationKit is a MediaWiki extension that uses ContentHandler, special pages, and EditPage extensions to make it easier to organize on-wiki projects.

Content models
CollaborationKit implements two novel content models: CollaborationHubContent and CollaborationListContent. Both of these content models represent data in JSON according to the schemas defined in CollaborationHubContentSchema.php and CollaborationListContentSchema.php respectively. Pages of either content type are validated against their respective schemas through the schema validation library in EventLogging. Note, however, that this library does not work perfectly, especially since it is not caught up with the latest version of the JSON Schema specification. There are long term plans to integrate a schema validation library in core MediaWiki and use that instead; see Requests for comment/JSON validation.

Each of these content models use a serialization format called text/x-collabkit for editing. The details depend on the content model, but the basic thing is that each high-level key-value pair is represented as one or more lines, separated by a specific number of hyphens, with the order of these blocks of text pre-determined by the serialization/deserialization methods. For sub-objects, key-value pairs are presented, one per line, as key=value. For arrays, each item in the array is one line, with key-value pairs separated by vertical pipes. In some cases, this serialization is used to build the editing interface, minimizing exposure of code-like syntax to the user. The exact JSON stored in the database can be edited directly through the "Edit as JSON" tab, or through the  parameter in the edit URL.

Most forms of page interaction that exist with MediaWiki also work with these content models. They can be deleted, moved, etc. like other pages.

CollaborationHubContent
The CollaborationHubContent model represents "Collaboration Hubs," which are landing pages for on-wiki projects. Collaboration Hubs are essentially shell pages that then transclude other pages, including little content of its own.

The pages that are transcluded are called "features," corresponding to sections on the hub page and (usually) corresponding to project subpages. In addition to the named features, two additional pages are transcluded: the /Announcements subpage, which is wikitext, and /Members, which is CollaborationListContent. These subpages are created upon hub creation via Special:CreateCollaborationHub, but if those subpages don't exist, then the hub handles it gracefully.

Example
A sample Collaboration Hub:

{   "display_name": "Project Hampshire", "introduction": "A WikiProject dedicated to the ceremonial county of Hampshire in the United Kingdom.", "footer": "", "image": "Flag_of_Hampshire.svg", "colour": "red", "content": [ {           "title": "WPX:WikiProject Hampshire/Requests", "image": "quotes", "display_title": "Requests" },       {            "title": "WPX:WikiProject Hampshire/Resources", "image": "book", "display_title": "Resources" }   ] }

Let's break this down:
 * display_name – a name to show other than the actual page title (optional)
 * introduction – Some introductory content. Supports any arbitrary wikitext.
 * footer – To display on the bottom of the page. Supports any arbitrary wikitext; you could use this for navboxes, categories, etc.
 * image – An image to serve as an "icon" for the project. Can be any image accessible on the wiki or a key for one of the "canned icons."
 * colour – Can be one of 23 preset colours. The image and colour collectively are the "hub theme"
 * content – the main body of the project. Each object in this array is a "feature," a project subpage containing something of interest to the project. The parameters for the content object include:
 * title – the full name of the page to transclude. Generally, features are subpages of the project, but they do not have to be!
 * image – an icon to use in the hub's auto-generated table of contents, typically one of the canned icons. As with the image parameter above, you could also use an on-wiki image for this (though it probably won't look as good)
 * display_title – Normally the subpage name would be displayed as the section header and table of contents item (e.g. "X" for "Project:Foo/X"), but this parameter lets you override that.

Announcements are shown below the introduction, members in a box to the right of the introduction, the table of contents and the transcluded pages below that, and the footer below that.

Internally there is an attribute called displaymode, set to one of "normal" or "error." It is only used internally. If the page fails to validate against the schema, the displaymode is set to error, and an error message is shown with a text serialization of the offending JSON, internally represented by an attribute called errortext.

Also defined as part of the schema is a "scope" object, allowing the user to define the scope of the project in terms of included and excluded page titles and categories (including to a specified category recursion depth). If you add a scope object to a hub it will pass validation but not actually do anything.

Transclusion behavior
A note on how features are transcluded: there are different rules for how this works with different content models:
 * wikitext – Generic wikitext pages only have the lead section ("section zero") transcluded. This is intentional; this allows the lead to serve as a teaser or summary, with a link in the feature header that leads people to the full page contents.
 * CollaborationHubContent – Only the hub image and hub introduction are presented. This is partly in keeping with the above principle but also because of implementation issues involving how the MediaWiki parser handles transclusion.
 * CollaborationListContent – In the future this will be highly configurable, but for the time being, the default transclusion options are used, which are defined in the  method and currently are:
 * includeDesc (include list description text): false
 * maxItems (maximum number of items to show per column): 5
 * defaultSort: random
 * offset: 0
 * tags: none
 * mode: normal (as opposed to no-img)
 * showColumnHeaders: true
 * iconWidth: 64px

In theory any arbitrary content model could be transcluded, but this has only been tested on the above three (and the UI does not exactly encourage it).

Editing
CollaborationKit extends the EditPage class to build a custom editor for CollaborationHubContent. The first step is to convert the stored JSON into the text/x-collabkit serialization. The format is as follows:

Display name --- Introduction --- Footer --- Image --- Colour --- Features

That's precisely 23 hyphens between each section. To use the above section, the serialization would look something like this:

Project Hampshire --- A WikiProject dedicated to the ceremonial county of Hampshire in the United Kingdom. ---

--- Flag_of_Hampshire.svg --- red --- WPX:WikiProject Hampshire/Requests|image=quotes|display_title=Requests WPX:WikiProject Hampshire/Resources|image=book|display_title=Resources

Each of those sections correspond to input fields on the form.

Let's look more closely at the bottom section. Each line corresponds to a transcluded feature. The first, unlabeled parameter is the full page title. Each subsequent parameter is optional, but includes  which can be a filename or canned icon, and   to use a label other than the subpage name.

If you have JavaScript enabled, the colour dropdown and image input field are replaced with a "hub theme" widget. The image selector in particular makes use of the "media search" widget, formerly a part of VisualEditor but upstreamed to core MediaWiki in 2016.

CollaborationListContent
The CollaborationListContent model represents "Collaboration Lists," which are lists of page titles with associated pictures, notes, and other attributes. The main goal of this content model is to allow users to develop lists of pages to work on that are visually appealing and can be selectively transcluded. In other words, one does not need to transclude the entire list. Through the  ParserFunction, different criteria can be applied. This way, large lists can be embedded in other pages without it being overwhelming.

Example
{   "options": {}, "displaymode": "normal", "description": "Help us with these tasks!", "columns": [ {           "items": [ {                   "title": "Heather Macy", "notes": "Predicted class: Stub" },               {                    "title": "Judith L. Rapoport", "notes": "Predicted class: C"               }, {                   "title": "Catherine King (scientist)", "notes": "Predicted class: C"               } ],           "label": "Assess for quality", "notes": "Determine the quality of these articles" },       {            "items": [ {                   "title": "Monika Auweter-Kurtz", "notes": "German physicist • More information on Wikidata" },               {                    "title": "Maja Žvanut", "notes": "Slovene art historian • More information on Wikidata" },               {                    "title": "Selda Ekiz", "notes": "No English description available • More information on Wikidata" }           ],            "label": "From Wikidata", "notes": "Automatically generated list of missing articles" }   ] }

There are three metadata fields and one large object containing the structured contents of the page:
 * The options object is used to define specific configuration settings for the list. Currently this is not exposed in the UI, and is mostly used internally to render lists in different ways (including by the parserfunction).
 * displaymode is either normal (for a regular list) or members (for a list of project members). There is also a third setting, error, used internally for hubs that do not pass validation. The display mode is not surfaced in the UI, and this is intentional – users should generally not arbitrarily switch between regular lists and member lists. (They technically can, but it makes no sense to.)
 * description refers to opening introductory text and accepts any arbitrary wikitext

Finally, there is the columns array, which is where the content lives. All lists are divided into columns; a flat list technically has one column. There is a hardcoded limit of ten columns on each page. As with the page-wide metadata, each column has these fields:
 * label, which is the text label for the column (optional).
 * notes, a short description placed under the label (also optional).

Within each object in the columns is the list array, containing a list of page titles. Each entry on the list is represented as an object and there is a hardcoded limit of 2000 items per column. The data in each object includes:
 * title, the full page title
 * notes, some short text to place after the title, typically describing the task that needs to be done (optional)
 * image, an image to be shown alongside the list entry. This is optional, and CollaborationKit will try to fetch an image through the PageImages API (if installed and enabled) if no image is specified. Otherwise, a generic page icon will be displayed.
 * link, to link to a page other than the stated page title. This is optional because by default the link is to the corresponding on-wiki page title. If you set this to false there will be no link at all, but you can only do this while editing the JSON directly because otherwise the deserializer will assume you are linking to an on-wiki page called "False" ( as opposed to  ). External links are not supported.

Editing
As with CollaborationHubContent, CollaborationListContent makes use of a serialization to build an editing interface. One of the goals of this interface is to facilitate "bulk editing," whereby someone can dump a whole list of page titles at once instead of inserting them one at a time.

The format is as follows: Description --- Options --- Contents

Within the options block, each individual option is a key-value pair separated by an equals sign. Display mode, technically not an option but a separate attribute altogether, is also included in this block, written in all caps as  to "signify its specialness" according to a comment in the source code.

The contents section is further divided into columns. As noted above, all lists have at least one column. An individual column looks like this:

-~-~- Assess for quality|notes=Determine the quality of these articles - Heather Macy|Predicted class: Stub Judith L. Rapoport|Predicted class: C Catherine King (scientist)|Predicted class: C

That's nine hyphens, a tilde, a hyphen, another tilde, nine more hyphens, a line of text, and then a line of 21 hyphens. Within the line of text is the name of the column, with an optional  parameter separated by a vertical pipe. There must be text here. If you do not want a header for this column, set the name to  in all lowercase letters; this will be interpreted as "do not present a title here." (You do not need to do this when editing JSON directly.) Each individual list item is per line, following a syntax of  with no named parameter. Parameters are separated by vertical pipe; the remaining parameters (image, link) are optional.

Users can also edit the list directly through the list itself through the use of JavaScript widgets. Associated with each list items are three buttons: one to delete the item, another to re-position it via drag-and-drop, and another to edit its values. Those edits are made via the MediaWiki API. At the bottom of each column is an "add" button that makes use of the same UI as the per-item editor.

Members lists
Members lists, i.e. lists with a display mode of "members," behave slightly differently. Members lists typically have only one column that is virtually divided into two columns: one for active members and one for inactive members. Activity is based on whether the user edited the wiki in the past 30 days. The idea is not so much that they are actively participating on the project, but that they are around on the wiki in general. MediaWiki handles this sorting automatically. However, because technically there is only one column in the source code, this leads to some oddities that are accounted for in the code by creating special exceptions for members lists. This should be resolved through proper support for dynamic columns, i.e. columns that are automatically created based on some criteria.

Members lists, instead of having a button for adding a page to the list, has a button for adding a user to the list, with usernames accepted as input instead of page titles necessarily. There is also a button on top of the list for the user to join at the click of a button; this button is mirrored on the hub, and disappears if the user is a member of the project already. (In the absence of JavaScript, the join button on the hub just links to the members' list's action=edit.)

Drag-and-drop rearranging of users is disabled; users are automatically sorted into alphabetical order.

Transcluding lists on other pages
Collaboration Lists can be transcluded just like any other page on the wiki.

However, through the  ParserFunction, a user could transclude a subset of the list. The syntax is as follows:. Parameters are optional and include:
 * defaultSort – natural (i.e. the order on the page) or random. Default is random.
 * maxItems – maximum number of items to show. Default is 5.
 * mode – default is "normal," or you could set it to "no-img" to disable images.
 * tags – show only items of these tags. By default there is no filtering according to tag. (Not implemented in UI.)
 * Use a plus sign to require both. For example,  means that both the X and Y tags are required in each item shown.
 * Use a duplicate parameter call to require either. For example,  means that an item can have either tag X or tag Y (or both).

These parameters currently do not work:
 * columns – columns to show (shows all by default)
 * Currently leads to a fatal error
 * offset – which position on the list to start on, counting up from 0. Default is 0, i.e., do not offset the list.
 * Currently leads to an exception
 * iconWidth – the width of the icons, in pixel. Default is 64px.
 * Currently leads to an exception
 * showColumnHeaders – default is true
 * Currently no-op
 * includeDesc – should the list description be shown? Default is false.
 * Currently no-op

Features not exposed in the UI
The schema includes alternative options for sorting, including custom sortkeys for items that include sorting criteria names, suggesting sorting criteria other than random or natural. However, this is not exposed in the UI and may not be functional.

There is also a tagging system where items can be assigned freeform tags, with transcluded lists then only showing items that match the stated tag(s). This system is perfectly functional and can be used by specifying a tags array in the item JSON; for example:

{   "title": "Borzoi", "notes": "Why the long face?", "tags": [ "Russian" ] }

Then, items with only that tag can be embedded through the  ParserFunction above. However, since this requires being able to edit JSON, it is not widely advertised as a feature.

Special pages
CollaborationKit implements two special pages: Special:CreateCollaborationHub and Special:CreateHubFeature.

They're extensions of the FormSpecialPage class and are not terribly exciting.

Usage of these special pages requires editing and page creation rights.

CreateCollaborationHub
Special:CreateCollaborationHub creates three pages: the hub (a CollaborationHubContent page), the members list (a CollaborationListContent members list page with the creator as the first member) and an announcements page (a wikitext page) that is embedded in the hub. The members list is created at subpage /Members and the announcements page is created at subpage /Announcements. All three pages are created at once. Much of the UI here is also used on the specialized editing interface for the CollaborationHubContent model.

Collaboration Hub creation is restricted to certain namespaces; by default, these namespaces are the project and user namespaces.

While this special page is shown in the list of special pages, it does not show up in any other part of the UI. In the long run it will probably be shown more prominently users, but the exact implications of this need to be sorted out first.

CreateHubFeature
Special:CreateHubFeature is the main UI for creating features, i.e., hub sections that are also standalone subpages. Users can arrive on this page by one of three ways:
 * 1) Visiting the page directly
 * 2) Clicking the "Add feature" button at the bottom of a Collaboration Hub
 * 3) Clicking the "Create feature" button when a non-existent feature is embedded on a Collaboration Hub

If the page is accessed by (2), then the name of the Collaboration Hub is automatically filled on the page. If by (3), then both the name of the Hub and of the feature are autofilled. The feature name refers to a subpage of the named Collaboration Hub, and the creation will not succeed unless the parent page is a Collaboration Hub. That said, arbitrary pages can be embedded as features onto Hubs; they just can't be created arbitrarily through this particular UI.

Created pages can either be generic wiki pages (of the wikitext content model) or CollaborationListContent models.

Canned icons
These icons are loaded by the extension. Each icon can be found on Git; click on each filename to see a preview.

Each icon is referred to by a keyword in lieu of a filename. In the linked list, those filenames correspond to the keyword, but without the  suffix. For example,  refers to.

In the CollaborationKitImage class, these canned icons are treated differently from other images. Instead of being directly embedded, they are embedded as divs, with the image set as the background of that div.

Colours
Collaboration Hubs can be assigned one of 23 colours. These colours are defined in modules/ext.CollaborationKit.mixins.less (except for black, which is "special").

When a colour is set, the icons are also recoloured to match the theme.

These are the colours: