Extension:External Data

The External Data extension allows MediaWiki pages to retrieve, filter, and format structure data from one or more sources. These sources can include an external URLs, a regular wiki page, an uploaded file, an LDAP directory and a database.

Parser functions
The extension has eight parser functions:
 * #get_web_data retrieves CSV, GFF, JSON or XML data from a URL and assigns it to variables that can be accessed on the page.
 * #get_external_data does the same as #get_web_data, with a different syntax; this function is now deprecated.
 * #get_db_data retrieves data from a database
 * #get_ldap_data retrieves data from an LDAP server
 * #external_value displays the value of any such variable.
 * #for_external_table cycles through all the values retrieved for a set of variables, displaying the same "container" text for each one.
 * #store_external_table cycles through a table of values, mimicking a call to the Semantic Internal Objects extension's #set_internal function for each row (Semantic Internal Objects must be installed for this function to work).
 * #clear_external_data erases the current set of retrieved data

Download
You can download the External Data code, in .tgz format, here.

You can also download the code directly via Git from the MediaWiki source code repository. From a command line, you can call the following:

You can also view the code online here.

Installation
To install this extension, create an 'ExternalData' directory (either by extracting a compressed file or downloading via Git), and place this directory within the main MediaWiki 'extensions' directory. Then, in the file 'LocalSettings.php', add the following line:

Authors
External Data was written by Yaron Koren (reachable at yaron57@gmail.com), Michael Dale, and David Macdonald. Important code contributions were also made by Siebrand Mazeland, Ryan Lane, Chris Wolcott, Jelle Scholtalbers, Kostis Anagnostopoulos and Nick Lindridge.

Retrieving data
Data can be retrieved from three different sources: from a web page containing structured data (including a page on the wiki itself), from a database, and from an LDAP server.

#get_web_data - CSV, GFF, JSON, XML
To get data from a web page that holds structured data, call the parser function #get_web_data. It can take the following syntax:

{{#get_web_data: url=

You should also add a line like the following, to set the expiration time of the cache, in seconds; this example line will cache the data for a week:

String replacement in URLs
One or more of the URLs you use may contain a string that you would prefer to keep secret, like an API key. If that's the case, you can use the array $edgStringReplacements to specify a dummy string you can use in its place. For instance, let's say you want to access the URL "http://worlddata.com/api?country=Guatemala&key=123abcd", but you don't want anyone to know your API key. You can add the following to your LocalSettings.php file, after the inclusion of ExternalData:

Then, in your call to #get_web_data, you can replace the real URL with: "http://worlddata.com/api?country=Guatemala&key=WORLDDATA_KEY".

Whitelist for URLs
You can create a "whitelist" for URLs accessed by #get_web_data: in other words, a list of domains, that only URLs from those domains can be accessed. If you are using string replacements in order to hide secret keys, it is highly recommended that you create such a whitelist, in order to prevent users from finding out those keys by including them in a URL within a domain that they control.

To create a whitelist with one domain, add the following to LocalSettings.php:

To create a whitelist with multiple domains, add something like the following instead:

Security
By default, #get_web_data allows for HTTPS-based wikis to access plain HTTP URLs, and vice versa, without the need for certificates (see Transport Layer Security on Wikipedia for a full explanation). If you want to require the presence of a certificate, add the following to LocalSettings.php, below the inclusion of External Data:

Using XPath
In some cases, the same tag or attribute name can be used more than once in an XML file, and you only want to get a specific instance of it. You can do that using the XPath notation. To do it, you just need to add the parameter "use xpath", and then have each "external variable name" in the "data=" parameter be in XPath notation, instead of just a simple name.

We won't get into the details of XPath notation here, but you can see a demonstration of "use xpath" here.

#get_db_data - retrieve data from a database
The parser function #get_db_data allows retrieval of data from external databases. This function executes a simple SELECT statement and assigns the results to local variables that can then be used with the #external_value or #for_external_table functions.

A note about security: - If you are going to use #get_db_data you should think hard about the security implications. Configuring a database in LocalSettings.php will allow anyone with edit access to your wiki to run SQL select statements against that database. You should use a database user that has the minimum permissions for what you are trying to achieve. It is possible that complex SQL constructions could be passed to this function to cause it to do things vastly different to what it was designed for. You should know what you are doing before enabling this function.

Configuration
Each database being accessed needs to be configured separately in LocalSettings.php. For normal databases (i.e., everything except for SQLite), add the following stanza for each database:

Where:


 * ID is a label for this database which is used when calling #get_db_data
 * server URL is the hostname on which the database lives
 * DB type is the type of database, i.e. mysql, postgres, mssql, oracle, sqlite, db2 or mongodb
 * DB name, username and password are details for accessing the database.

An example of a set of values would be:

The following optional settings can also be added:

Example values for these variables are:

Support for database systems
MySQL, Postgres (i.e. PostgreSQL), Oracle, DB2 and MongoDB should work fully by default (though there are special syntax limitations for MongoDB - see below). For MS SQL/SQLServer and SQLite, you may need to perform some special handling.

For Microsoft SQLServer, if it doesn't work by default, there are three options that may help: ...and replace them with:
 * If the wiki is running on Windows, you can install Microsoft drivers for PHP for SQL Server.
 * If the wiki is running on Linux, the MSSQLBackCompat extension may help. If you use this extension, you will need to specify "mssqlold" (not "mssql") as the database type.
 * If you get messages at the top that look like, "Warning: Invalid argument supplied for foreach in ...\includes\db\DatabaseMssql.php", this code change might help: in the MediaWiki file includes/db/DatabaseMssql.php, look for lines that look like:

Meanwhile, to connect to SQLite, you need something like the following in LocalSettings.php:

For MongoDB, there are no special connection parameters, although the username and password may be optional.

Usage
To get data from an external database, call the following:

An explanation of the fields:


 * db - the identifying label configured in LocalSettings.php (this parameter used to be known as "server")
 * from - an SQL from clause
 * where - an SQL where clause (optional)
 * limit - a number, limiting the number of results (optional)
 * order by - an SQL "order by" clause (optional)
 * data - mapping of database column names to local variables (syntax: localVariable=databaseColumn - i.e. "strName" is the name of the database column in the example below).

An example call, using the "employee database" example from above:

MongoDB is a non-SQL (or "NoSQL", if you prefer) database system, but it can be accessed using the SQL-like syntax of #get_db_data. There are some restrictions and differences, however, for the "where" clause:
 * only "AND"s can be used, not "OR"s
 * for the "LIKE" comparison, no text should be placed around the comparator - it should look like "Username LIKE Jo", not "Username LIKE '%Jo%'".

#get_ldap_data - retrieve data from LDAP directory
The parser function #get_ldap_data allows retrieval of data from external LDAP directories. This function executes LDAP queries and assigns the results to local variables that can then be used with the #external_value function. It currently only handles a single row of results, and so is most appropriate for querying directories for data about individual users.

A note about security: - If you are going to use #get_ldap_data you should think hard about the security implications. Configuring an LDAP server in LocalSettings.php will allow anyone with edit access to your wiki to run queries against that server. You should use a domain user that has the minimum permissions for what you are trying to achieve. Wiki users could run queries to extract all sorts of information about your domain. You should know what you are doing before enabling this function.

Configuration
You need to configure each LDAP server in LocalSettings.php. Add the following stanza for each server:

Where:


 * domain is a label to be used when calling #get_ldap_data
 * myDomainuser and myDomainPassword are credentials used to bind to the LDAP server
 * [basedn] is the base DN used for the search.

Example:

Usage
To query the LDAP server add this call to a wiki page:

Where:


 * domain is the label used in LocalSettings.php
 * filter is the ldap filter used for the search
 * data is the mappings of LDAP attributes to local variables

An example that retrieves a user from with Win2003/AD, using a userid passed to a template:

Note that #get_ldap_data will only retrieve one result.

Displaying data
Once you have retrieved the data onto the page, from any source, there are two ways to display it on the page: #external_value and #for_external_table.

Displaying individual values
If this call retrieved a single value for each variable specified, you can call the following:

As an example, this page contains the following text:

.
 * Germany borders the following countries and bodies of water:
 * Germany has population.

The page gets data from a URL at semanticweb.org, generated by the Semantic MediaWiki extension. That URL contains the following text:

Germany,"North Sea,Denmark,Baltic Sea,Poland,Czech Republic,Austria,Switzerland,France,Luxembourg,Belgium,Netherlands","82,411,000",3.5705e+11 m²

The page then uses #external_value to display the 'bordered countries' and 'population' values; although it uses the #arraymap function, defined by the Semantic Forms extension, to apply some transformations to the 'bordered countries' value (you can ignore this detail if you want).

Displaying a table of values
The data returned by #get_web_data or #get_db_data (#get_ldap_data doesn't support this feature) can also be a "table" of data (many values per field), instead of just a single "row" (one value per field). In this case, you can call the function #for_external_table to display it. For example, this URL at semanticweb.org contains information similar to that above, but for all the countries in Africa instead of just one country. Calling #get_web_data with this URL, with the same format as above, will set the local variables to contain arrays of data, rather than single values. You can then call #for_external_table, which has the following format:

...where "expression" is a string that contains one or more variable names, surrounded by triple brackets. This string is then displayed for each retrieved "row" of data.

For an example, this page contains a call to #get_web_data for the semanticweb.org URL mentioned above, followed by this call:

The call to #for_external_table holds a single row of a table, in wiki-text; it's surrounded by wiki-text to create the top and bottom of the table. The presence of " | " is a standard MediaWiki trick to display pipes from within parser functions; to get it to work, you just have to create a page called "Template:!" that contains a single pipe. There are much easier calls to #for_external_table that can be made, if you just want to display a line of text per data "row", but an HTML table is the standard approach.

There's one other interesting feature of #for_external_table, which is that it lets you URL-encode specific values, by calling them with instead of just. For instance, if you wanted to show links to Google searches on a set of terms retrieved, you could call:

This is required because standard parser functions can't be used within #for_external_table - so the following, for example, will not work:

Clearing data
You can also clear all external data that has already been retrieved, so that it doesn't conflict with calls to retrieve external data further down the page. The most likely case in which this is useful is when data is retrieved and displayed in a template that is called more than once on a page. To clear the data, just call " ". Note that the ":" has to be there at the end of the call, or else MediaWiki will ignore the parser function.

There is no way currently to clear the values for only one field; #clear_external_data erases the entire set of data.

Using External Data with Semantic MediaWiki
A common approach is to use External Data in conjunction with the Semantic MediaWiki extension: the data is retrieved using External Data, then stored using Semantic MediaWiki tags, so it can then be queried, aggregated, mapped etc. on the wiki. If you take this approach, you should note a common problem, which is that the data stored by SMW does not get automatically updated when the data coming from the external source changes. The best solution for this, assuming you expect the data to change over time, is to create a cron job to call the SMW maintenance script "SMW_refreshData.php" at regular intervals, such as once a day; that way, the data is never more than a day old.

If you want to store an entire table of data in a single page, instead of just individual values, you should use the #store_external_table function. This function works as a hybrid of the #for_external_table function and the #set_internal function from the Semantic Internal Objects extension (that extension must be installed for this function to work). A call to it looks a lot like a call to #set_internal (see the Semantic Internal Objects page for documentation), but it loops over each row, and uses variables, in the same way as #for_external_table. You can see a demonstration of this function on the page fruits semantic data; the call to #store_external_table on that page looks like:

Common problems

 * If the call to #get_web_data or #for_external_table isn't returning any data, and the page being accessed is large, it could be because the call to retrieve is getting timed out. You should set the $wgHTTPTimeout flag in your LocalSettings.php file (which represents a number of seconds) to some number greater than 3, its default value. You could call, for instance:




 * If the data being accessed has changed, but the wiki page accessing it still shows the old data, it is because that page is being cached by MediaWiki. There are several solutions to this: if you are an administrator, you can hit the "refresh" tab above the page, which will purge the cache. You can also easily disable caching for the entire wiki; see here for how. Finally, if you wait long enough (typically no more than 24 hours), the page will get refreshed on its own and display the new data.

Version history
External Data is currently at version 1.5. See the entire version history.

Bugs and feature requests
You should use the Semantic MediaWiki mailing list, semediawiki-user, for any questions, suggestions or bug reports about External Data. If possible, please add "[ED]" at the beginning of the subject line, to clarify the subject matter.

(Although the External Data extension in general is independent of Semantic MediaWiki, the fact that it uses Semantic Internal Objects for one of its functions, along with the fact that the two extensions are often used together, means that External Data can be considered part of the Semantic MediaWiki "family" for communication purposes.)

You can also send specific code patches to Yaron Koren, at yaron57@gmail.com.

Translating
Translation of External Data is done through translatewiki.net. The translation for this extension can be found here. To add language values or change existing ones, you should create an account on translatewiki.net, then request permission from the administrators to translate a certain language or languages on this page (this is a very simple process). Once you have permission for a given language, you can log in and add or edit whatever messages you want to in that language.

Links

 * Presentation at Wikimania 2009