Sunday, 3 March 2013

Trees in Google Apps Script UI


Trees in Google Apps Script

The tree widget in the Apps Script user interface presented me with a few issues. I persevered to get a solution that worked for me.

What did I want?

  • Tree element can be selected
  • Selection reflected by a change of style attributes (eg bold, red etc)
  • Selection of one element turns selection of previous element off
  • Take an action based on the selection made (a handler)

Tree Widget

Apps Script UI does provide a Tree widget and Google helpfully tells you it is based on GWT. I suspect that if you are an expert in GWT and its documentation you would not be using Apps Script.
Initially, I just set the tree branches to text with no widget (code below). This provided a standard indication that I had clicked (selected) the tree branch - a blue highlight. Unfortunately the selection handler that is fired, currently only tells you that the tree has been selected and not which branch or leaf. There is a long outstanding issue for this. EDIT 25/4/13. Now Fixed
function doGet() {
 var start = new Date();
 var app = UiApp.createApplication();
 var target = app.createLabel().setId("target");  
 var panel = app.createAbsolutePanel();
 // create tree
 var selhandler = app.createServerHandler("selector").addCallbackElement(panel);
 var tree = app.createTree().setAnimationEnabled(false).setId('FullTree').setTag("").addSelectionHandler(selhandler);
 var handler = app.createServerHandler("selector").addCallbackElement(tree);
 // build a tree
 for (i=0;i<100;i++){
   var str = "label"+(i+1);
   var str1 = "item"+(i+1);
   //  create level 1 branch
   var item1 = app.createTreeItem(”TEXT”).setId(str1).setState(true,true);
   // add level 2 branches to level 1
   addsub(app,item1,tree);
   // add level 1 branch to tree
   tree.addItem(item1);
 }
 panel.add(tree);
 panel.add(target);
 app.add(panel);
 Logger.log("Elapsed:"+(new Date() - start));
 return app;
 
}

Putting a label widget in the branches instead of the plain text allowed a click handler to be used. In the handler, parameter.source provides an indicator of the item clicked. But branch is no longer highlighted when selected so the user does not get feedback of what is selected.
At this point, I thought of setting the styling for selected/ not-selected myself. What was I thinking! You can use a client handler on click to set the attributes of the label widget when you click it. Unfortunately, client handlers do things to the source widget or statically named target widgets so you can’t remember the currently selected item and set its styling in a toggle action.
Back to the server handler then. Yes, if you have remembered the previous state, you can use the server handler to set the style accordingly. BUT, the only way that you can know the state in the server handler is to pass it via the parameters. This led me to the final problem, which was that the users would be able to click more than one widget faster than the server handler can deal with it. Multiple parallel server handlers are fired and the “previous state” is dependent on timing … instead of a toggle operation, the user interface can get multiple widgets marked as selected. The only way around this is to restyle all the widgets to the unselected state and then style the selected widget. To say this makes for an unresponsive interface is an understatement.

Solution

We need a widget that has built in “selected” behaviour and to put that in the tree. So use a radio button for each of the tree elements.
  • Highlighted (and filled button) when selected - fast, in browser, change
  • Exclusive selection built-in
  • Fires click handler for server action
  • Developer can change base style
  var label1 = app.createRadioButton("XOX",str).setId(str).setName("SameGroup").addClickHandler(handler).setTag(str).
   setStyleAttributes({'fontWeight':'bold','fontSize':'14pt', "color" : "black","fontFamily":"arial,sans-serif"});
Note that the name of each radio button has to be the same to put them into a common group for the toggle action to work.

Wednesday, 13 February 2013

Mixing User Information and Private Information in Google Apps Script

Going back to real coding. Well Google Apps Script anyway:)
Google Apps Script presents a few hurdles to an application designer if you want to work in the users' context with information derived from your private documents or domain.I needed to work with
  • some information derived from a document within my protected Google Apps document store  (document private to me, within a domain where sharing documents outside domain is forbidden)
  • the identity of user (may be outside domain but with a Google Account)

The identity of a user is available from Session.getActiveUser().getEmail() but this will fail in interesting ways unless the script is published to run under the user id of the user running the script.

The private document cannot be shared outside the domain, and in this particular case, access to the full document is restricted to one user within the domain. Scripted access to the information on the document must be done by a script published  to run as that privileged user.

So we need two scripts that talk to each other. One doing the conversation with the user and one operating as me in the background. UrlFetchApp.fetch() provides the means for one script to call another with parameters. ContentService.createTextOutput() provides the means to set the response to a regular format like JSON or XML.

The following scripts demonstrate the mechanics.

1. The front end UI interaction with the users

function doGet() {
 var app = UiApp.createApplication();
 var email = Session.getActiveUser().getEmail();
 var label = app.createLabel('Your email address is '+email);
 app.add(label);
 // get the private data object
 var obj = callUserWithPermission(email);
 Logger.log(JSON.stringify(obj));
 // put out data originating from private world and data round tripped from user world to private world and back
 var label2 = app.createLabel('App effective user was '+obj.available + " Originating user was " + obj.actual);
 app.add(label2);
 // Proceed with business on user side .... initial UI here
 return app;
}


function callUserWithPermission(email) {
 try {
 // use the run as privileged user script
    var response = UrlFetchApp.fetch("https://script.google.com/macros/s/AKfgfgfgmlxQXUP1OLr-BEs014S7b6gVxdhfdhfshdhfdVzM/exec?who="+encodeURIComponent(email));
    var parsedResponse = JSON.parse(response.getContentText());
    return parsedResponse;
 }
 catch(e) {
    Logger.log("error "+JSON.stringify(e))
 }
}

This script must be deployed so that it is run as the script user and available to anyone. It will insist on a Google Account login and acceptance by user of exposure of the email id.

2. The back end interaction with private domain documents

function doGet(e) {
 // see content service simple example
 // receive the info from "run as user" script as parameter "who"
 try {
    var app = UiApp.createApplication();
    var effective = Session.getEffectiveUser().getEmail();
    var result = {
     available: effective,
     actual: e.parameter.who
    };
    // DO the business function and add info to result
    return ContentService.createTextOutput(JSON.stringify(result))
    .setMimeType(ContentService.MimeType.JSON);
 }
 catch(ex) {
    throw new Error("In testgetprivate doGet()"+ex.message)
 }
}


This script must be deployed so that it is run as the script owner and available to anyone including anonymous users. This certainly should give you something to think about … this script can be run by anyone, from anywhere who knows the, admittedly difficult to guess, URI. Is the information returned very sensitive (not in my case)? Is the risk of exposure (very slight unless the developer leaves the source code lying around) balanced by the ease of implementation. Alternatives requires a whole authentication mechanism built into your app.

You may find that you cannot deploy a standalone script like this because of the "no sharing of documents beyond domain" restriction set up by administrator. In that case, put the script into a public site and deploy it from there.

Wednesday, 27 June 2012

Innovation Blackout

In the Harvard Business Review Blog Evade an Innovation Blackout, Jordan Cohen points out the difficulty of engaging effort for new ideas when the people with the ability to develop those ideas are up to their asses in aligators.
In a financial downturn, the razor-gangs that decend on the organisation are a big inhibitor of necessary change at just the wrong time. Any innovation is seen as something to be avoided or delayed rather than embraced as a means of getting out of a failing operation.

Monday, 25 June 2012

Backup Google Apps II

Continuing from   Backup Google Apps , I have been taking a look at Backupify.
This product meets a key component of my disaster recovery scenario. I am assuming that the worst that can happen to a Google Account is that users and the administrator lose access to it and, perhaps through the action of a 'bad person', Google cannot be persuaded to restore the account access.
Backupify can be accessed through a login independently of Google Apps which provides an alternative route to the backups if the 'bad person' has locked users out of the primary google apps files. Documents and Mail can be downloaded from Backupify as an alternative to restoring to the apps account so there is a measure of comfort that if the worst happens there will be a recovery path.
However there are some fish-hooks...

  • Because Backupify is accessible from Google Apps single sign-on, this convenience would be available to the bad person who could then lock the legitimate user out of Backupify*.
  • To support the download capability, Backupify transforms the Google Apps documents (creating xls files for Google Apps spreadsheets for example). This has unfortunate side effects, as there is not a one-one relationship between Google documents and the MSOffice equivalent so the round trip will lose
    1. any scripts developed in google apps spreadsheets
    2. the effect of google functions in spreadsheets
    3. charts in spreadsheets
    4. external references to shared or published documents

So there still does not appear to be an equivalent in Google Apps of the conventional off-site backup that will allow a recovery of all information in a disaster situation.
Is losing access to the Google Account a scenario that should be considered? I can think of a few possible events. In no particular order of likelihood or Machiavellian complexity:
  • Side effect of law enforcement action in US (eg Megaupload
  • Destructive action by a Google Apps super administrator 
  • A 'bug' in Google Apps

 [Update] * Backupify recognised the issue and responded (sensibly in my opinion)
Currently, we are looking to improve security with an extended identity verification that would allow you to regain access to your account faster in the case that this happened. We do not have this in the product now, but in the meantime if you want to send us a photo of your picture ID then we can store that internally and securely with your account. That way in the future is someone did gain unauthorized access to your Backupify account via Google Apps login and they changed the Backupify password that you set then we could work with you and use the photo ID that you sent to verify your true identity and get you access to your account again.
so we can expect there to be a process that could circumvent the issue of losing access to the backify account.

Wednesday, 13 June 2012

Backup of Google Apps

I am looking at the need for backup and recovery capability for organisations that manage their information and operations through Google Apps. 

Classically, backup for the unforeseen event comprises a resource remote from the primary information that can be used to support the business operations in the event of the non-availability of the primary source. Analysis of risk and impact of losses can be used to determine how long that you are able to do without the primary source and how much you should be prepared to pay for recovery within that time. Generally, only broad classifications of risk to the primary information is considered (destruction of data centre; denial of service etc).
In “cloud” services, we are buying a measure of protection of our information assets from the provider whether that is Google, Microsoft or Waikikamukau Data Services. The trust we can place in these organisations is related to their competence and also the size of the pain that the organisation will feel when your primary information is unavailable. For example, if Google reported that it could not recover from a failure of just one of its data centres, there would be a massive loss of confidence affecting the viability of the multibillion dollar company.
So we might safely assume that Google will be able to restore your account in event of a catastrophe in their domain, but user mishandling of information is another matter and firmly in the user’s court. For this the user needs a strategy to suit their needs for information assurance.

Implementing some form of backup for Google Apps will require one or more of

  • Maintaining an inhouse backup storage server to backup cloud based documents to local hard disk  or another storage supplier like Amazon.
  • Use a 3rd party service like Backupify or Spanning to copy and restore Google Apps documents/files.
  • Something to confirm that your backup policy is adhered to


In the case of Spanning Backup there are a few issues to be aware of.

  • When a document is recovered it is a new Google Apps document. Any references to the original will not be replaced by the restore operation. This will affect Sites that link to Apps documents, shares of the original, and published documents.
  • When a shared document is recovered, the new document is retains the shares of the original but the ownership changes to the user that performed the restore.
  • When a restore is performed, a folder is created to contain the restored documents. This folder is treated like any other, including taking part in subsequent backup cycles. There are opportunities for confusion about which version of a document is which.
  • The backup can only be used to recover to Google Apps and not as a path to another service

There will be good reasons for these issues and the design fits well with organisations where the individual user is responsible for their own environment (like with a personal computer). However, those familiar with IT-managed filestore could be surprised, unpleasantly.

Monday, 4 June 2012

Privacy Watchdog with Teeth?

Lets take computer privacy breaches seriously here in New Zealand. Give the Privacy Commission some teeth and send appropriate messages to the likes of ACC.
A recent case in the UK resulted in a significant fine being levied on a National Health Trust which failed to destroy sensitive data on 1000 hard disks before releasing them. More worrying was that they thought that they could contract out of the responsibility by using a 3rd party to facilitate the disposal.
Here in New Zealand, we get investigations but no sense that responsibility for the protection of sensitive data is sheeted home to senior management. The pressure on organisations that mishandle sensitive data is reduced by the requirement that the “complainant can show that they have suffered harm” rather than that there was a breach. Only “if the harm is significant, a complainant might be able to claim that they are entitled to compensation”. Note that there is no actual entitlement to compensation nor a means of making orders like that made in the UK case. The best we can hope for is a sound drubbing of the Minister by the the capital’s press but even that has been lacklustre.

Wednesday, 16 May 2012

Smart Meter Privacy

John Udel has raised Smart Meters up the conciousness ladder in a timely post.
Smart meters are new. But we can’t afford to think that every new technology rewrites all the rules, requiring new legislation which, as we know, can never keep pace with innovation. Here’s a powerful simplifying rule: It’s your data. That’s the default. And you shouldn’t need to be a do-it-yourselfer to assert ownership. Even if you use a utility-supplied meter, as most people will, it’s still your data.
It's your data??? I am not sure that it is so simple. It is 'their' accounting record. The meter certainly reveals something about the occupier, which is not necessarily the other party to the power company contract, and there are certainly undesirable uses for the information - for example knowing that the house has a pattern of occupation.
There needs to be an auditable protocol for dealing with the handling of the broad swathe of surveillance data from smart power meters to smart parking and cctv but I don't think it starts with "It's my data"!
I am looking forward to the views of the NZ Privacy Commisioner on this. I would hope that some clear direction is given so that surveillance data which can be associated with individuals is treated in the same way as Personal Information and therefore covered by the privacy principles. Attaching some teeth to the principles would be good too, but one step at a time.