• Home
  • Consultancy
  • Contact
  • Using Elgg’s REST-like API

    August 25th, 2009 by Marcus Povey

    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

    The Export Interface

    August 24th, 2009 by Marcus Povey

    One of the more hidden features of Elgg is the Export interface.

    In a nutshell this interface provides an export view for Entities, Metadata, Annotations and relationships which can provide a convenient way of accessing data objects in a machine readable form.

    Endpoints

    The export url is constructed in different ways for entities, relationships and metadata.

    All endpoints begin with:

    http://yoursite.com/export/[VIEW]/

    Where [VIEW] is the format you want the data exported in – e.g. json, opendd, php or default.

    Entities
    To export a GUID simply add it to the end:

    http://yoursite.com/export/[VIEW]/[GUID]/

    Annotations & Metadata

    Metadata and annotation can be exported by providing the type (‘annotation’ or ‘metadata’) and the appropriate ID.

    http://yoursite.com/export/[VIEW]/[GUID]/[annotation|metadata]/[annotation_id|metadata_id]/

    Relationships
    Follows the same format as above, but with [GUID] being the first guid in the relationship – in essence the entity to which the relationship “belongs”.

    http://yoursite.com/export/[VIEW]/[GUID]/relationship/[relationship_id]/

    Security

    Some items of data (for example user passwords) are restricted from this export view. Exactly what is output by an output view is governed by $object->getExportableValues(); which returns a list of exportable fields in the entity.

    In addition, access permissions on the object are respected – meaning that if you can’t see an item in Elgg, you will not be able to see it in the export view either.

    Post processing Elgg views – Trac tags example

    July 6th, 2009 by Marcus Povey

    Trac_Logo_512x512There are a number of ways that an Elgg plugin developer can manipulate views via the powerful Elgg views system.

    Most Elgg programmers are by now familiar with extending or replacing existing views, or providing new views for new objects etc.

    But what if you just wanted to make a simple tweak to an existing view – for example to replace all instances of rude words in a feed article, or to turn passive links into active ones?

    Well, fortunately Elgg provides a post processing hook for views which can do just that.

    After every view has been generated, the framework will trigger a plugin hook called “‘display’, ‘view’”. This hook is passed a parameter ‘view’ which contains the name of the view being processed (eg. object/blog).

    The contents of the view are passed in the $returnvalue variable which you can perform any processing on before returning it from the hook.

    I have just uploaded a Trac tags plugin to the Elgg community site which provides a good example of this.

    The Trac tags plugin is a tiny plugin which uses the views post processing hook to turn Trac links (e.g. #xxxxx for tickets and [xxxxx] for changesets) into active links into your repository, and here’s how – first we register the hook:

    function tractags_init()
    {
    ....
    // Register our post processing hook
    register_plugin_hook('display', 'view', 'tractags_rewrite');


    // define views we want to rewrite codes on (means we don't have to process *everything*)
    $CONFIG->tractags_views = array(
    'object/thewire',
    'object/blog'
    );

    ....
    }

    Then in our handler looks something like this:

    function tractags_rewrite($hook, $entity_type, $returnvalue, $params)
    {
    global $CONFIG;

    $view = $params['view'];

    if (($view) && (in_array($view, $CONFIG->tractags_views)))
    {
    // Search and replace ticket numbers
    $returnvalue = preg_replace_callback('/(#)([0-9]+)/i',
    create_function(
    '$matches',
    '
    global $CONFIG;

    return "<a href=\"{$CONFIG->trac_baseurl}ticket/{$matches[2]}\">{$matches[0]}</a>";
    '
    ), $returnvalue);

    // Search and replace changesets
    $returnvalue = preg_replace_callback('/(\[)([0-9]+)(\])/i',
    create_function(
    '$matches',
    '
    global $CONFIG;

    return "<a href=\"{$CONFIG->trac_baseurl}changeset/{$matches[2]}\">{$matches[0]}</a>";
    '
    ), $returnvalue);

    return $returnvalue;
    }
    }

    I’m sure you will be able to come up with some much more interesting uses!

    Image from the Trac project.

    Loosely coupled Elgg extensions (Captcha example)

    June 15th, 2009 by Marcus Povey

    A new CAPTCHA approachOne thing we try and do when working on a new Elgg feature is – where we can – couple things together as loosely as possible and provide hooks for third party developers to extend Elgg and fill in any blanks.

    A good example of where this has been done is the newly introduced Captcha functionality available in the latest nightly testing builds of Elgg.

    The Captcha functionality is provided by a module which extends a view called “input/captcha“. This view is blank by default but is used in several places such as user registration and the lost password form.

    This means two things; firstly that if a Captcha module isn’t installed or enabled then forms behave normally, and secondly it becomes a trivial matter for third party modules to provide their own Captcha functionality.

    This same mechanism is how the URL shortener module works by the way.

    Next, the Captcha module extends a number of actions to require a correctly validated Captcha code. This list itself is the product of a plugin hook which returns an array of actions which require Captcha validation:

    $actions = array();
    $actions = trigger_plugin_hook('actionlist', 'captcha', null, $actions);

    ...

    function captcha_actionlist_hook($hook, $entity_type, $returnvalue, $params)
    {
    if (!is_array($returnvalue))
    $returnvalue = array();

    $returnvalue[] = 'register';
    $returnvalue[] = 'user/requestnewpassword';

    return $returnvalue;
    }

    The reason why the list of actions is provided this way is twofold, firstly it lets modules use Captcha functionality in their own code through a generic interface, and secondly it is harder to spoof than looking for some marker in the form code.

    The Captcha itself injects a server generated token into the form, which together with the user’s response to the characters generated in an image are used to validate that the user is indeed human.

    As we can see, Elgg asks to be provided with a Captcha if one is available by including a specific view, but is agnostic as to where (or indeed if) this functionality is supplied.

    By using the techniques available to an Elgg programmer I was able to loosely couple the Captcha system to Elgg in such a way that a third party can easily use the same techniques to provide a more advanced module.

    Happy coding!

    Image “A new Captcha approach” by XKCD

    Extending actions in Elgg

    May 8th, 2009 by Marcus Povey

    Those eagle-eyed developers who have been tracking the Elgg core SVN may have noticed that I have recently committed a bunch of captcha related changes, including a simple captcha module.

    I just thought I’d write a quick post about it as this module makes use of a bit of Elgg functionality which has been around for a while, but that I know a number of plugin developers have missed.

    Namely, the ability to extend actions.

    When the Elgg framework calls an action the Action handler triggers a plugin hook called “action” before executing the action itself. This hook looks like this:

    $event_result = true;
    $event_result = trigger_plugin_hook('action', $action, null, $event_result);

    Where $action is the action being called. If the hook returns false then the main action will not be executed.

    The captcha module uses this to intercept the register and user/requestnewpassword actions and redirect them to a function which checks the captcha code. This check returns true if valid or false if not (which prevents the associated action from executing).

    This is done as follows:

    register_plugin_hook("action", "register", "captcha_verify_action_hook");
    register_plugin_hook("action", "user/requestnewpassword", "captcha_verify_action_hook");

    .
    .
    .

    function captcha_verify_action_hook($hook, $entity_type, $returnvalue, $params)
    {
    $token = get_input('captcha_token');
    $input = get_input('captcha_input');

    if (($token) && (captcha_verify_captcha($input, $token)))
    return true;

    register_error(elgg_echo('captcha:captchafail'));

    return false;
    }

    As you can see, this lets a plugin extend an existing action without the need to replace the action itself. In the case of the captcha plugin it allows the plugin to provide captcha support in a very loosely coupled way.

    Happy coding!

    « Previous PageNext Page »
    All content is © Copyright Marcus Povey 2008-2010 and released under a Creative Commons licence unless otherwise stated.

    Creative Commons License