Introduction

In this page you can find details about the new lists protocol, made to solve Issue201 (on Google Code).

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 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"]
}
  • 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:

  1. 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).
  2. 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).
  3. 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.