blob: 461984199abb303178a7574b42d65e24f2b5bec0 [file] [log] [blame]
#summary Details about the Lists protocol
#labels 2013DeveloperDoc
= Introduction =
In this page you can find details about the new lists protocol, made to solve Issue201.
= Protocol format =
The JSON expected should have the following general format (see Details):
{{{
{
"configuration" : {},
"features": {
"cookie_service": {
"enabled": <boolean>
},
"column_search": {
"enabled": <boolean>,
"regexp": <boolean>
},
"columns_show_hide": {
"enabled": <boolean>
},
"search_dialog": {
"enabled": <boolean>
},
"csv_export": {
"enabled": <boolean>
},
"global_search": {
"enabled": <boolean>,
"element_path": <jQuery_selector>
},
"global_sort": {
"enabled": <boolean>,
"element_paths": {
"column": <jQuery_selector>,
"asc_desc": <jQuery_selector>
}
},
"hide_headers": {
"enabled": <boolean>
}
},
"templates": {
"<column>": "<template>"
},
"operations" : {
"buttons": [
{
"bounds": [],
"id": "",
"caption": "",
"type": "",
"parameters": {}
}
],
"row": {
"type": "",
"parameters": {}
}
},
"data": {
"": [
{
"columns": {}
"operations": {
"buttons": {
"": {
}
},
"row": {},
"row_buttons" {
"": {
}
}
}
}
]
}
}
}}}
= Details =
This is a step by step description of the protocol.
The very general format is a JSON object with four different objects, indexed by keys, with the following format:
{{{
{
"configuration" : {},
"features": {},
"templates": {},
"operations": {},
"data": {}
}
}}}
----
== *configuration* ==
The _configuration_ object is passed *verbatim* to [http://www.trirand.com/blog/ JQGrid], which is the jQuery plugin that is used by Melange to show lists. In this way, it's the Python end that decides every aspect of the appearance of the list. All the informations about the configuration object can be found in [http://www.trirand.com/jqgridwiki/doku.php?id=wiki:jqgriddocs JQGrid Wiki pages], pages concerned are [http://www.trirand.com/jqgridwiki/doku.php?id=wiki:conventions conventions], [http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options options] and [http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options colmodel]. However here is a brief example explained:
{{{
"configuration": {
"colNames": ["Key", "Link ID", "Name", "Program Owner"],
"colModel": [
{name: "key", index: "key", resizable: true},
{name: "link_id", index: "link_id", resizable: true},
{name: "name", index: "name", resizable: true},
{name: "program_owner", index: "program_owner", resizable: true}
],
rowNum: 4,
rowList: [4, 8],
autowidth: true,
sortname: "link_id",
sortorder: "asc",
height: "auto",
multiselect: true,
toolbar: [true, "top"]
}
}}}
* *colNames*: is an *array of strings* which defines the label of each column as shown to the end user.
* *colModel*: is an *array of objects*, which defines more details about each column. The number of objects in the array should be the same as the number of strings in the _colNames_ array. The order of the columns detailed in the _colModel_ array should be the same of what's in the _colNames_ array. In this example, each object of the _colModel_ has a *name*, which should be the same name as defined in the "data - columns" object of the protocol. The *index* string (normally same as _name_ string) defines which is the column that is used to order the whole list when clicking on the column. *resizable* is a simple boolean field, which determines if the column of the final list is resizable or not.
* *rowNum*: is an *integer*, and defines a default of how many rows should be shown for each page of the list
* *rowList*: is an *array of integers*, which defines a list of choices to the end user of number of elements to be displayed for each page. In this case, the end user can choose to display 4 or 8 elements for each page of the list.
* *autowidth*: is a *boolean*, which defines if the width of the list should be dinamically changed to fill the whole page.
* *sortname*: is a *string* that defines which is the default order of the list (should be one of the _name_ string in the _colModel_ array of objects.
* *sortorder*: should be a *string* chosen from _asc_ and _desc_, to define the default order of the list.
* *multiselect*: is a *boolean* that should be present if the list should have a column of checkboxes (for example to have multiple deletion of rows at once). See _data/operations/buttons_, _operations/buttons_ (if the list should be a multiselect one) and _operations/row_ (if the list should not be a multiselect one).
* *toolbar*: is an *array of a boolean and a string*. This should be present as displayed in the example if there are buttons to be displayed on top of the list (for example to show add/edit/delete options, see _data/operations/buttons_ details and _operations/buttons_ details.
----
== *features* ==
The _features_ object (optional) is used to enable/disable and/or set options to specific features of Melange lists. This can contain:
* *cookie_service*: is an (optional) *object* which can contain only one key, *enabled*, which is a boolean. By default is enabled, so if the object is not present the cookie service will be enabled, to be back-compatible with current lists. If it's enabled, the lists will use a cookie to store/retrieve their state between sessions.
* *column_search*: is an (optional) *object* which should contain to keys, *enabled* and *regexp*, which are booleans. By default is enabled, so if the object is not present the column search will be enabled, to be back-compatible with current lists. If it's enabled, each column in the list can be filtered using a input (either text or select) which is appended at the top of the column itself. If *regexp* is false (true by default) the top-right checkbox which enables regexp search won't appear. In this way column search can be enabled with or without the option to search by regexp.
* *column_show_hide*: is an (optional) *object* which can contain only one key, *enabled*, which is a boolean. By default is enabled, so if the object is not present the button to Show/Hide columns will be enabled, to be back-compatible with current lists. If it's enabled, a button at the bottom of the lists will appear to open a dialog to show/hide columns in the lists, as well as a checkbox on the upper right to enable/disable regexp search.
* *search_dialog*: is an (optional) *object* which can contain only one key, *enabled*, which is a boolean. By default is enabled, so if the object is not present the button to Show the advanced search dialog will be enabled, to be back-compatible with current lists. If it's enabled, a button at the bottom of the lists will appear that opens a dialog box to do an advanced search inside the lists.
* *csv_export*: is an (optional) *object* which can contain only one key, *enabled*, which is a boolean. By default is enabled, so if the object is not present the button to export to CSV will be enabled, to be back-compatible with current lists. If it's enabled, a button at the top right of the lists will appear that opens a dialog box with a CSV export of the list.
* *global_search*: is an (optional) *object* which can contain two keys, *enabled*, which is a boolean, and *element_path*, which is a jQuery selector that should select an input text which should be present in the page. By default is *not* enabled to be back-compatible with current lists. If it's enabled, an event of type "keyup" will be bound to the input text element to look into all the columns in the list ("OR" search) for its value. *HINT:*: if you specify the same *element_path* for more than one list in a single page, all lists with the same element selector will be filtered by the same value.
* *global_sort*: is an (optional) *object* which can contain two keys, *enabled*, which is a boolean, and *element_paths*, which is an object which can contain one or two key-value pairs. Both values need to be jQuery selectors that point to unique HTML select tags already present in the page. The selector for the key *column* should point to the select which needs to be populated with a list of the columns present in the list. The selector for the key *asc_desc* should point to the select which needs to be populated with the options "Ascending" and "Descending". There is no need to specify both keys. By default is *not* enabled to be back-compatible with current lists. If it's enabled, an event of type "change" will be bound to the select(s) to reorder the list accordingly.
* *hide_headers*: is an (optional) *object* which can contain only one key, *enabled*, which is a boolean. By default is enabled, so if the object is not present the lists will show the toolbar (in which the global buttons are attached) and the column headers, to be back-compatible with current lists. If it's disabled, those headers will be hidden.
----
== *templates* ==
The _templates_ object (optional) is used to define which columns between the ones defined in *colModel* will be substituted with a string that is built from a template. At the moment, it supports only Django-like variables like "{{ variable }}". Each variable *must* be a valid column name in *colModel*. If you have three columns with the following data _longDescription = ""_, _title = "My Title"_ and _name = "My Name"_ and use the following _templates_ object:
{{{
"templates": {
"longDescription": "My name is {{ name }} and my title is {{ title }}"
}
}}}
then, in the final table, the _longDescription_ cell for that particolar row will display:
_My name is My Name and my title is My Title_
----
== *operations* ==
The _operations_ object is used in the protocol to define general behaviour of the list for buttons and rows actions. That is, you can define a number of buttons that will appear on top of the list, e.g. for add/edit/delete links (only consistent if you define {{{toolbar: [true, "top"]}}} in the _configurations_ object), and a default behaviour when a user clicks on a row of the list. Here an example explained:
{{{
"operations": {
"buttons": [
{
"bounds": [0,"all"],
"id": "add",
"caption": "Add a user",
"type": "redirect_simple",
"parameters": {
"link": "http://add1",
"new_window": true
}
},
{
"bounds": [1,1],
"id": "edit",
"caption": "Edit user(s)",
"type": "redirect_custom",
"parameters": {
"new_window": true
}
},
{
"bounds": [1,"all"],
"id": "delete",
"caption": "Delete user(s)",
"type": "post",
"parameters": {
"url": "/user/roles",
"keys": ["key","link_id"],
"refresh": "current"
}
},
{
"bounds": [0,"all"],
"id": "diff",
"caption": "Diff between revisions",
"type": "post",
"parameters": {
"url": "/something",
"keys": ["key","link_id"],
"redirect": "true"
}
}
],
"row" : {
"type": "redirect_custom",
"parameters": {
"new_window": true
}
}
}
}}}
=== *buttons* ===
The _buttons_ *array of objects* defines general behaviour for buttons displayed on the top of the list. Each object in the array has the following details:
* *bounds*: is an *array of integers* or an *array of an integer and the keyword "_all_"*. This array defines a range in which the button is enabled. The _"all"_ keyword is dynamically and automatically substituted with the number of records in the list.
* *id*: is a *string* that should define a _unique id_ for the button which is referred by _data/operations/buttons_ object. This unique id is limited to the _buttons_ array. IDs with the same name in different lists on the same page don't conflict.
* *caption*: is a *string* as shown to the end user (this can be customized for each row, see _data/operations/buttons/caption_)
* *type*: is a *string* which defines the behaviour of the button. At the moment there are three different behaviours you can choose from for buttons: _"redirect_simple"_, _"redirect_custom"_ and _"post"_ (see below).
* *parameters*: is an *object* that defines (using key/value pairs) parameters for the method _type_ chosen.
Following a list of methods allowed for buttons:
# *redirect_simple*: this type of behaviour is normally used when the button should be static and should always redirect to the same url (e.g. "Add user"). Parameters accepted are of the following types: {{{"link": "http://whatever"}}} (that is, a *string* with the url the button should redirect to) and {{{"new_window": true}}} (that is, a *boolean* which define whether the redirected page should be shown in a new window or not).
# *redirect_custom*: this type of behaviour is used when the button should dinamically change the link where the user should be redirected depending on which row is chosen (e.g. "Edit user") (see _data/operations/buttons/link_ and _data/operations/buttons/caption_). Only parameter accepted is {{{"new_window": true}}} (that is, a *boolean* which define whether the redirected page should be shown in a new window or not).
# *post*: this type of behaviour is used when the button should send something to the backend (e.g. a list of entities to delete). Parameters accepted are of following types: {{{"url": "http://whatever"}}} (that is, a *string* with the url the button should send data to the backend), {{{"keys": ["key","link_id"]}}} (that is, an *array of strings*: each string should be one of the _name_ key in the _colModel_ array, see _configuration/colModel_ and those will define which data should be sent back, using an array of objects, to the backend for each entity selected client side), {{{"refresh": "current"}}} is a *string* which determines whether the current table should be refreshed or not: at the moment only "current", "all" (refreshes all tables in the page) and "anyintegerindex" (which means "1", "2",etc, as in the index of the list in the page) options are allowed, support for single "row" refreshing will be made some time in the future. {{{"redirect": "true"}}} means that the server wants the client to be redirected to another page after sending data to the server, and the URL is not known at the moment of the creation of the configuration. The client will send the data requested and expect a JSON like this:
{{{
"data": {
"url": "http://new_url_to_redirect_to"
}
}}}
=== *row* ===
Row actions are allowed only if no {{{multiselect: true}}} is found in the _configuration_ object. Only *redirect_custom* method is allowed, with the same options as for _operations/buttons_. Details to define the url of each row in _data/operations/row_ details.
----
== *data* ==
The _data_ object is used in the protocol to contain the actual data that should be displayed in the list and details about buttons/row click behaviour for each row (when *redirect_custom* is chosen as a method for button/row on _operations/buttons_ and/or _operations/row_). Here an example explained:
{{{
"data": {
"": [
{
"columns": {
"key": "key_test7",
"link_id": "test7",
"name": "Admin Test Example",
"program_owner": "melange"
},
"operations": {
"buttons": {
"edit": {
"caption": "Edit key_test user",
"link": "http://edit1"
}
},
"row": {
"link": "http://my_row_edit_link"
},
"row_buttons": {
"<dest_column>": {
"buttons_def": {
"edit": {
"caption": "Edit this user",
"type": "redirect_simple",
"classes": ["class_1", "class_2"],
"parameters": {
"link": "http://www.google.com",
"new_window": True
}
},
"delete": {
"caption": "Delete this user",
"type": "redirect_simple",
"parameters": {
"link": "http://www.youtube.com",
"new_window": True,
}
}
},
"template": "Template for {{ edit }} and {{ delete }}",
},
"<dest_column>": {
"buttons_def": {
"add": {
"caption": "Add this user",
"type": "redirect_simple",
"classes": ["class_1"],
"parameters": {
"link": "http://www.libero.it",
"new_window": True
}
}
}
}
}
}
}
]
}
}}}
So, _data_ is an object indexed by key. In the example, you can see what the first iteration (fetching data from backend) should return. The key is actually an empty string. This key should represent the *GAE key of the last entity in the former iteration*. That is, if the last entity fetched is "key_test7" (as in the example), the next iteration should contain "key_test7" as the key of the array of objects in the _data_ object. So, the _data_ returned is indeed an *array of objects*, which has an empty string as index (if it's the first batch) or the GAE key of the last entity fetched in the former batch. Each object (which actually represents a row in the final list) has two objects inside:
=== columns ===
This is the actual data for the row that should be shown to the end user. Each field is indexed by the _name_ chosen in the _colModel_ array (see _configurations/colModel_). It's obviously important that each entity contains exactly the same number and the same names as defined in the _colModel_ array.
=== operations ===
This objects defines details for each row if the chosen _type_ of method for buttons and/or rows (defined in _operations/buttons_ and/or in _operations/row_) is *redirect_custom*. The examples very well explains what is needed.
* *buttons*: is an *object* which has more than one object inside, indexed by a key. This key is the _id_ defined formerly in _operations/buttons/id_. In this case, the id of the button chosen is _"edit"_. redirect_custom method needs a *caption* (a *string*), which defines what the caption of the button should show when the user clicks on that very row, and a *link* (a *string*) which will be the page where the user should be redirected when that very row is selected and the button is clicked.
* *row*: is an *object*, which contains only a *link* option, which is a *string* that contains an URL. That string defines where the user should be redirected when clicking on a row.
* *row_buttons* is an *object* which has more than one object inside, indexed by a key. This key is a *string* which is the column in which this button has to be added to. It must be a valid column _name_ in the _colModel_ array (see _configurations/colModel_). The value is an *object*, which should contain the keys *buttons_def* and (optional) *template*. The value of *buttons_def* should be objects indexed by a key. This key is for reference and used afterwards by the *template* key, and makes it unique between all the row buttons. Each button definition has a *caption* (a *string*) that defines the caption of the button once is appended to the column. *type* and *parameters* follow the same rules as per the global _buttons_ in _operations_ objects (see _operations/buttons_). Only *redirect_simple* is enabled at the moment. It is suggested not to have any row operation if you enable row buttons, as to do so will have no effect when clicking the buttons, since the row operation will take precedence. The *classes* array is *optional* and can contain a list of CSS classes for the button. *templates* is an (optional) *string* which should contain the template that is appended to the cell. You can use common Django variables syntax to refer to row buttons for that specific column (see _features/templates_ object for reference). If a Django variable in the template refers to a button that is not present for that column then it's ignored and printed as it is.