joomla/joomla-cms — pull request #2508 by nikosdion (2013-11-13 11:57:39+00:00)
Title: Allow commercial (for a fee) components to use the Joomla! extensions update feature [Tracker 32684]

# Update support for commercial extensions

**IMPORTANT:** See Joomla! tracker item 32684 at http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=32684&start=0

## Technical analysis

### Status quo

The Joomla! extension update system, up to and including Joomla! 3.2.0, had no support for commercial extensions. This stemmed from the way the updater works. The developer can only specify an update source in his XML manifest which gets saved in the `#__update_sites` table. The update source is an XML file containing the available version numbers, the platform they support (e.g. Joomla! 3.2 and 2.5) and a download URL. When an update is found, all information *except* the download URL is saved in the `#__updates` table.

When a user requests that an extension be updated, through com_installer, the XML update source is parsed *again*, the download URL is fetched from it and com_installer subsequently downloads and installs the package from that URL.

The big problem with this approach is that the download URL is common for all users. This is perfect for free extensions. It is, however, problematic for commercial extensions. If the developer provides a public URL, common to everyone, for downloading their extension then any user can download and install their commercial software without payment. All a user needs to know is the common download URL. Providing a user-specific download URL means that the developer needs to provide a different XML update source file for each and every of their users. This is impractical and has led to the development of bespoke updaters for commercial extensions.

The major problem with the current situation is the degradation of the user experience. All free extensions' updates are visible in the Control Panel page of Joomla! where the user can easily install them. Commercial extensions' updates are visible only in the respective extensions' interfaces in non-standard ways, essentially being invisible to the user.

### Proposal

Considering how important updates are for site security and the benefits of a unified user experience I propose a small scope, backwards compatible change in JUpdater to resolve this situation.

The solution consists of the addition of an extra_query column in the `#__update_sites` and `#__updates` table. It contains additional URL query parameters which are appended to the update download URL. Thus the update XML files and the XML manifests of the extensions need not be modified. The only requirement is that the developer populates the column in the `#__update_sites` table using any mechanism he/she deems appropriate, thereby giving absolute control to the developer over the interface to provide the information necessary for constructing the additional query parameters.

## Instructions to developers

### Operational concept

The concept of the commercial extensions support hinges on the methodology most of us follow to provide downloads to our commercial extensions, namely the addition of one or more special URL query parameters to our download URLs. The special URL query parameters are used to authenticate the user to our download systems, allowing the download without having the user interactively log in to our sites. For example, if the generic download URL is:

http://www.example.com/com_something.zip

let's say that we need to tack `username=sith&key=darth_vader` to it in order to authenticate the user. In other words the direct download URL would be:

http://www.example.com/com_something.zip?username=sith&key=darth_vader

The generic download URL will continue to exist in your update XML file, e.g.

Something
Something, something, something component
com_something
component
2.1.0

http://www.example.com/something.html

http://www.example.com/com_something.zip

stable

Example, Inc.
http://www.example.com/

Updates

administrator

You would, therefore, need to add `username=sith&key=darth_vader` to the `#__update_sites` table's `extra_query` column.

### Code example

The code you need to write only needs to do the following:

* Get the extension_id for your extension (note: this applies to any kind of extension, not just components)
* Get the parameters to construct the extra URL query parameters string, e.g. from a component's configuration
* Update the `#__update_sites` table
* Flush any existing update records for your extension

Here is a code example for the fictional component com_something, fetching the username and key from the eponymous Options page parameters.

// Get the component information from the #__extensions table
JLoader::import('joomla.application.component.helper');
$component = JComponentHelper::getComponent('com_something');

// Fetch the configuration options and construct the extra URL query
$username = $component->params->get('username', '');
$key = $component->params->get('key', '');

$extra_query = '';

if (!empty($username) && !empty($key))
{
// Important: you are supposed to use & instead of a straight ampersand
$extra_query = 'username=' . urlencode($username) . '&key=' . urlencode($key);
}

// Load the update site record, if it exists
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('update_site_id')
->from($db->qn('#__update_sites_extensions'))
->where($db->qn('extension_id').' = '.$db->q($component->id));
$db->setQuery($query);
$updateSite = $db->loadResult();

if ($updateSite)
{
// Update the update site record
$query = $db->getQuery(true)
->update($db->qn('#__update_sites_extensions'))
->set(array(
'extra_query'	=> $extra_query,
'enabled'	=> 1,
'last_check_timestamp'	=> 0,
))
->where($db->qn('extension_id').' = '.$db->q($component->id));
$db->setQuery($query);
$db->execute();

// Delete any existing updates (essentially flushes the updates cache for this update site)
$query = $db->getQuery(true)
->delete($db->qn('#__updates'))
->where($db->qn('update_site_id').' = '.$db->q($updateSite->update_site_id));
$db->setQuery($query);
$db->execute();
}

This code is relatively naïve (it doesn't check the existing update site) and is only meant as an example, not the definitive implementation.