Examples

Before going though all the events, all the operations that can be performed on objects, and the other python functionality that Rekall provides, this section has what is hopefully a representative set of examples of things that can be done with scripting.

Record Navigation the Proper Way

In earlier examples, the shorcut mechanism was used to create record navigation buttons, where the On Click property was set to #Click. This calls some standard code in the RekallMain.py modules. However, the same can be done directly, as shown in the code below, which implements next record functionality:

def eventFunc (button) :
    button.getBlock().doAction(3)
        

When the event function is invoked, the first argument is the button. From this the enclosing block is retrieved, and the code then invokes action 3, which is next record. This could be a little better done as in the next piece of code (and note that, from Rekall 2.0.1, the RekallMain module is automatically imported into event functions):

def eventFunc (button) :
    button.getBlock().doAction(RekallMain.actNext)
        

In fact, we can do this even more concisely in the latest versions of Rekall, as below. See Appendix E for the complete list of methods.

def eventFunc (button) :
    button.getBlock().nextRecord()
        

Doing things this way does not have any advantages over the shortcut (apart from the aesthetic point that it does not use a rather ad-hoc mechanism), unless you want to do something else at the same time. Suppose, for instance, that the form has a checkbox (named confirm) which must be checked before the user can move on to a different record. Then:

def eventFunc (button) :
    block = button.getBlock()
    check = block.getNamedCtrl("confirm")
    if check.getValue(block.getQueryRow()) != "1" :
        RekallMain.messageBox ("Please confirm first!")
        return
    block.doAction(RekallMain.actNext)
        

This code assumes that the button and the checkbox control are in the same block. The code gets the block, then locates the checkbox control, and then checks the value. There is currently no explicit isChecked method, but the checkbox control will return values 0 or 1. Whether or not the checkbox control is associated with a column from the table that the block retrieves data from (that is, whether it has an empty Expression property or not), it is still necessary to specify the query row, which is also retrieved from the block.

Now, there are two problems with this check in this code. Firstly, it would need to be replicated for all navigation buttons, although we could partially get around this by moving most of the code into a separate module, and just calling it from the event function. The second problem is more important, however; this code would not prevent the used clicking the toolbar next record button, or using a keyboard shortcut. However, we can get round both problems at once by moving the check to the block On Action event, which is invoked whenever an action (such as next record) is about to take place. The button event code can revert to the version without the test, and the block On Action code is then (where the ellipsis are replaced by the other relevant actions):

def eventFunc (block, action) :
    if action == RekallMain.actFirst or ... :
        check = block.getNamedCtrl("confirm")
        if check.getValue(block.getQueryRow()) != "1" :
            RekallMain.messageBox ("Please confirm first!")
            return 0
    return 1
        

If the On Action event returns false, then the action is aborted, so this code has the desired affect. More importantly, it handles any navigation buttons you might add, it works if the user uses the toolbar buttons, and it works if the user uses a keyboard shortcut.

(Re)Querying the database

When the user clicks the reload tool button, Rekall in effect excutes two block actions, startQuery and executeQuery. This is the same as if the user clicks the start query tool button and then the execute query tool button, without typing any query expressions into any of the fields.

Since these actions are available from scripts, you can set up a button to do exactly the same thing:

def eventFunc (button) :
    block = button.getBlock()
    block.startQuery()
    block.executeQuery()
        

This can used in order to set up queries. For instance, suppose that you have a form that shows information about people, and you'd like to have a button that selects only people who are exactly 21 years old. If the age is displayed in a field called age then you can use:

def eventFunc (button) :
    block = button.getBlock()
    block.startQuery()
    block.getNamedCtrl('age').setValue(block.getQueryRow(), '=21')
    block.executeQuery()
        

This has just the same effect as the user clicking the start query tool button, entering =21 into the appropriate field, and then clicking execute query. But, suppose that you are not actually displaying the age. Instead, you can (starting with Rekall 2.2.2) do:

def eventFunc (button) :
    block = button.getBlock()
    block.startQuery()
    block.setUserFilter ('age = 21')
    block.executeQuery()
        

The block.setUserFilter(expr) call sets a filter expression which will be added to the query used to retrieve data from the database. Note that it remains in force until it is explicitely changed, and it works in additions to any where term you specified when creating the underlying database query, and any search terms that the user might enter.

There is a similar block.setUserSorting(expr) call that will set an expression to be used to sort (order) the records that are retrieved from the database. Similarly, this remains in force until explicitely changed, and works in addition to any order term you specified for the underlying query.

Locking Fields

This example shows how to lock fields against update depending on some criteria. Suppose that we have a form which shows information about products (actually, this could be the Products form from the Orders demonstration database), and that we wish to stop the user from updating some fields for particular products. For the example, we'll use the product code to control this; the fields are locked if the product code is equal to one.

The event function code below is attached to the block On Display event, which is executed each time a row of data is displayed (and will be executed several times in succession if the block displays more than one row). The block should contain a control named Product which retrieves the product code from the server database; this might well be a hidden field. Essentially, this code executes each time the set of values in a row are displayed, and enables or disables the Quantity, DatePlaced and DateDispatched fields.

def eventFunc (block, qrow) :
    ordinary = block.getNamedCtrl("Product").getValue(qrow) != "1"
    block.getNamedCtrl("Quantity"      ).setEnabled (qrow, ordinary)
    block.getNamedCtrl("DatePlaced"    ).setEnabled (qrow, ordinary)
    block.getNamedCtrl("DateDispatched").setEnabled (qrow, ordinary)
        

Much in the same way as the previous example, since the code is attached to a block event, it works correctly however the user navigates through the data.

Roll Your Own Form

As has been remarked earlier, form and report definitions are stored in XML, which you can view and, if you wish, edit yourself. Another feature which follows on from this is the ability to write scripts which construct entire forms or reports that are customised for specific situations; in this example the script is embedded inside the On Click action of a button.

When executed below, the code prompts the user to select a field from the Client table of the Orders demonstration database (this is the RekallMain.choiceBox (....) call. It then constructs a form which shows a small form which displays then client company name plus the selected field, along with a pair of navigation buttons. For added spice, if the user selects Address then the form concatenates the address fields and displays them (the code here assumes that the underlying server database is MySQL). The last line of the script, button.getForm().openTextForm(form), opens the form that has been created.

def eventFunc (button) :
    name = RekallMain.choiceBox \
           (    "A client field, please:",
                [       "Telephone",
                        "Contact",
                        "Department",
                        "Address",
                        "PostCode"
                ]
           )
    if name == None :
        return
    legend = name
    if name == "Address" :
        name = "CONCAT(address1, ', ', address2, ', ', " + \
               "address3,', ',TownOrCity)"
    form = '<?xml version="1.0"?>' + \
           '<!DOCTYPE KBaseForm SYSTEM "kbaseform.dtd">' + \
            '<KBForm x="0" y="0" w="400" h="120" xmode="0" ymode="0"' + \
            '        name="UnnamedForm"' + \
            '        autosync="Yes"' + \
            '        rowcount="1" dx="0" dy="20" language="py"' + \
            '        caption="Client field: ' + legend + '" stretch="Yes">' + \
            '  <KBQryTable server="Self" table="Client" primary="ClientID"' + \
            '              order="Company"/>' + \
            '  <KBField  x="20" y="20" w="370" h="20" name="Company"' + \
            '            expr="Company" taborder="1" align="1"/>' + \
            '  <KBField  x="20" y="50" w="370" h="20" name="theField"' + \
            '            expr="' + name + '" taborder="1" align="1"/>' + \
            '  <KBButton x="20" y="80" w="70" h="30" name="Previous"' + \
            '            text="<" onclick="#Click"/>' + \
            '  <KBButton x="110" y="80" w="60" h="30" name="Next"' + \
            '            text=">" onclick="#Click"/>' + \
            '</KBForm>'
    button.getForm().openTextForm(form)
        

This is clearly not a trivial thing to do, and requires a fairly detailed knowledge of the XML that defines a form, but it illustrates one of the advanced things that Rekall can do. If you do want to do this, one way is to design a form in the formal way in order to get the basic layout, etc., correct, then use the XML for that form as the basis of the script.

There is work in progress to develop a set of python classes which can be used to do this more easily, for instance you would create a python form object, then add objects such as fields and buttons. This is essentially the XML DOM model.