in_ur_realityAnother one of Elgg‘s less documented but very powerful features is the ability to expose functionality from the core and user modules in a standard way via a REST like API.

This gives you the opportunity to develop interoperable web services and provide them to the users of your site, all in a standardised way.

The endpoint

To make an API call you must direct your query at a special URL. This query will be either a GET or a POST query (depending on the command you are executing), the specific endpoint you use depends on the format you want the return value returned in.

The endpoint:

http://yoursite.com/pg/api/[protocol]/[return format]/

Where:

  • [protocol] is the protocol being used, in this case and for the moment only “rest” is supported.
  • [return format] is the format you want your information returned in, either “php”, “json” or “xml”.

This endpoint should then be passed the method and any parameters as GET variables, so for example:

http://yoursite.com/pg/api/rest/xml/?method=test.test&myparam=foo&anotherparam=bar

Would pass “foo” and “bar” as the given named parameters to the function “test.test” and return the result in XML format.

Notice here also that the API uses the “PG” page handler extension, this means that it would be a relatively simple matter to add a new API protocol or replace the entire API subsystem in a module – should you be so inclined.

Return result

The result of the api call will be an entity encoded in your chosen format.

This entity will have a “status” parameter – zero for success, non-zero denotes an error. Result data will be in the “result” parameter. You may also receive some messages and debug information.

Exporting a function

Any Elgg function – core or module – can be exposed via the API, all you have to do is declare it using expose_function() from within your code, passing the method name, handler and any parameters (note that these parameters must be declared in the same order as they appear in your function).

Listing functions

You can see a list of all registered functions using the built in api command “system.api.list”, this is also a useful test to see if your client is configured correctly.

E.g.

http://yoursite.com/pg/api/rest/xml/?method=system.api.list

Authorising and authenticating

Most commands will require some form of authorisation in order to function. There are two main types of authorisation – protocol level which determines whether a given client is permitted to connect, and user level where a command whereby a user requires a special token in lieu of a username and password.

Protocol level authentication
Protocol level authentication is a way to ensure that commands only come from approved clients for which you have previously given keys. This is in keeping with many web based API systems and permits you to disconnect clients who abuse your system, or track usage for accountancy purposes.

The client must send a HMAC signature together with a set of special HTTP headers when making a call. This ensures that the API call is being made from the stated client and that the data has not been tampered with.

Eagle-eyed readers with long memories will see a lot of similarity with the ElggVoices API I wrote about previously.

The HMAC must be constructed over the following data:

  • The Secret Key provided by the target Elgg install (as provided easily by the APIAdmin plugin).
  • The current unix time in microseconds as a floating point decimal, produced my microtime(true).
  • Your API key identifying you to the Elgg api server (companion to your secret key).
  • URLEncoded string representation of any GET variable parameters, eg “method=test.test&foo=bar”
  • If you are sending post data, the hash of this data.

Some extra information must be added to the HTTP header in order for this data to be correctly processed:

  • X-Elgg-apikey – The API key (not the secret key!)
  • X-Elgg-time – Microtime used in the HMAC calculation
  • X-Elgg-hmac – The HMAC as hex characters.
  • X-Elgg-hmac-algo – The algorithm used in the HMAC calculation – eg, sha1, md5 etc

If you are sending POST data you must also send:

  • X-Elgg-posthash – The hash of the POST data.
  • X-Elgg-posthash-algo – The algorithm used to produce the POST data hash – eg, md5.
  • Content-type – The content type of the data you are sending (if in doubt use application/octet-stream).
  • Content-Length – The length in bytes of your POST data.

Much of this will be handled for you if you use the built in Elgg API Client.

User level tokens

User level tokens are used to identify a specific user on the target system, in much the same way as if they were to log in with their user name and password, but without the need to send this for every API call.

Tokens are time limited, and so it will be necessary for your client to periodically refresh the token they use to identify the user.

Tokens are generated by using the API command “auth.gettoken” and passing the username and password as parameters, eg:

http://yoursite.com/pg/api/rest/xml/?method=auth.gettoken&username=foo&password=bar

Anonymous methods
Anonymous methods (such as “system.api.list”) can be executed without any form of authentication, thus accepting connections from any client and regardless of whether they provide a user token. This is useful in certain situations and it goes without saying that you don’t expose sensitive functionality this way.

To do so set $anonymous=true in your call to expose_function().

Image “In UR Reality” by XKCD

Recently – both in my roll as a developer on the Elgg project, and as one of the organisers of Barcamp Transparency – I have found myself having to sign up for a whole bunch of accounts for various sites.

Each one asks me to fill in a profile, and each time I end up repeating myself. I am sick of it. This is the kind of thing OpenID was developed to partially solve, however I think this is overkill.

OpenID (as mentioned elsewhere) has problems and its uptake is declining. I rather think this is because it is trying to do far too much.

Gravatar on the other-hand is simple and to the point, requires the end user to do very little and is pretty damn simple to implement from a server point of view.

Could the same approach be used for profile fields? I think yes, and here’s how it might work…

  • First of all, we have a site somewhere which lets a user create an account and fill in their profile fields.
  • The profile comes pre-populated with common labels (name, description, location, interests etc), but lets users add extra fields if they like.
  • The service has a REST like API at the back end which accepts queries like: http://fooprofile.com/api/[field]/[md5 hash of email address]/ to which it returns a blob of text.
  • When a user creates a new account on bar.com, that site should attempt to pre-populate any profile fields with data from the service based on an md5 hash of their email address. These can of course be overridden locally.
  • Periodically bar.com should update its fields via the API, unless the user has overridden the profile field (or has otherwise selected not to do so).

Crucially with this light method, the user experience of the site remains pretty much unchanged and all the hard work is done magically in the background.

I also think that there is no need to specify what fields constitute a profile. The semantics of this will likely evolve naturally over time and there is no way to predict what extra fields will be needed. You wouldn’t dictate what tags someone would use, so why dictate profile fields?

In phase two of this you could easily imagine using OAuth to decide which fields a site has access to.

You could also imagine multiple providers being possible (providing the api was consistent). So when a user signs in to bar.com they are asked who there provider is – so they could select fooprofile.com or wibbleprofile.co.uk or any other provider. This would keep OpenID’s distributed nature, but without confusing the user too much – a url is always a url in this model.

So all that leaves is the single point authentication aspect as a distinct and separate problem, and one which must be solved in a way that is transparent to the user – perhaps an encrypted and public key authenticated token exchange using a similar technology as the above?

Just pondering….

Update: I have bashed together an example of the sort of thing I was talking about over here: http://skunk.marcus-povey.co.uk/aer/

Over on his blog, my good friend and colleague Ben has written a good post about bugtrackers. He is essentially complaining that there are currently none available that are good for both developers and end users.

Broadly speaking I agree with him. The two main players – Bugzilla and Trac – are both lacking. Bugzilla’s interface has notable usability issues, and trac too is somewhat lacking.

In both cases however, the core functionality of what a bugtracker actually does – a prioritised and editable todo list – works perfectly.

The problem is interface.

How do we create one that is useful to both developers (who need quite detailed settings) and end users (who need a simple interface and in many cases need a certain amount of hand holding in order to fill in a report which is useful to the developer)?

Thinking back to my usage of both Bugzilla and Trac – the answer is that we don’t.

Let me explain: I have used both Bugzilla and Trac in anguish on large projects for many many years, but I have hardly ever used the default interface – currently I use the excellent Mylyn (nee Mylar) for Eclipse. For me a bugtracker is a central todo list accessible from anywhere – combined with a central svn repo it becomes possible for me to continue to do work anywhere there is a computer and internet connection… invaluable if you spend any amount of time travelling.

It seems to me that a good approach would be to have the bug tracker entirely API driven (more so than it is now – which in many cases is a later bolt on), that way it would be possible to provide a variety of expert interfaces for developers and a simplified interface for end users – rather than having one interface try and do it all.

This interface should hold peoples hand and ask specific targeted questions to encourage non-programmers to provide reports which will be useful to developers.

Tagging (and tag clustering) could be a useful technique to then group issues together – making it easy to find related issues and to spot duplicates.

Building on some social technology to establish relationships between issues, comment around them and attach files and other media could also be useful.

If the underlining engine is the same this shouldn’t involve too much in the way of work duplication, but will allow for tighter integration with the tools and workflow people actually use.