In this page you can find details about the new lists protocol, made to solve Issue201 (on Google Code).
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" {
"": {
}
}
}
}
]
}
}
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": {}
}
The configuration object is passed verbatim to 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 JQGrid Wiki pages, pages concerned are conventions, options and 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"]
}
The features object (optional) is used to enable/disable and/or set options to specific features of Melange lists. This can contain:
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
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
}
}
}
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:
Following a list of methods allowed for buttons:
"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)."new_window": true (that is, a boolean which define whether the redirected page should be shown in a new window or not)."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 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.
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:
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.
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.