By Amol Ghatol on Wednesday, 09 September 2015
Category: Joomla Development

How to provide live updates for extensions in Joomla?

As a Joomla extension provider, we are always rolling out updates for our extensions. Whether they are minor bug fixes or major feature releases, its always recommended that customers stay up-to-date so that they get the best of features, bug fixes as well as security fixes.

Normally the only way to notify them would be via Blogs and Newsletters. However this needs the customer to do the process of updation manually. 

With Joomla however, there is a much easier and smarter way of doing this using the Updates system. This is already baked into the core of Joomla and can be used by any Joomla extension. There is a core way to achieve both notifications and automatic updates which you can read about here. It works pretty well for free extensions but needs you to manually maintain the updates manifest. We have updated the blog with some inputs from weebler to also demonstrate how you can do this with core Joomla. 

The Akeeba Release System from the awesome Akeeba team however can give this system a major shot in the arm by completely automating Manifest generation and maintenance and also support updates for commercial extensions

Here’s a quick tutorial on how you can use ARS to automate extension update notifications and live updates in your extensions.

In fact since Joomla 3x has become the standard, your end users might be completely missing your updates if you are not using the Joomla 3x notification system and rely on your own ‘In extension notifications’ Read more about this in our earlier blog.

Note: This Blog is purely targeted to developers!

Here is how it is done!

Follow these simple steps:

             Go to Components => Akeeba release system => Update Streams => New

 

Add following code in the model file

// Setup global variable
$this->updateStreamName = ‘Stream name’; // e.g JLike
$this->updateStreamType = 'extension'; // e.g. extension
$this->updateStreamUrl  = "link_to_your_update_stream_xml";
$this->extensionElement = 'element name';  //e.g com_jlike
$this->extensionType    = 'component'; 
/**
 * Refreshes the Joomla! update sites for this extension as needed
 *
 * @return  void
 *
 * @since  1.1.7
 */
	public function refreshUpdateSite()
	{
		// Extra query for Joomla 3.1 onwards
		$extra_query = null;
		if (preg_match('/^([0-9]{1,}:)?[0-9a-f]{32}$/i', $this->downloadid))
		{
			$extra_query = 'dlid=' . $this->downloadid;
		}
		// Setup update site array for storing in database
		$update_site = array(
			'name' => $this->updateStreamName,
			'type' => $this->updateStreamType,
			'location' => $this->updateStreamUrl,
			'enabled'  => 1,
			'last_check_timestamp' => 0,
			'extra_query'          => $extra_query
		);
		// For joomla versions < 3.1
		if (version_compare(JVERSION, '3.1', 'lt'))
		{
			unset($update_site['extra_query']);
		}
		$db = $this->getDbo();
		// Get current extension ID
		$extension_id = $this->getExtensionId();
		if (!$extension_id)
		{
			return;
		}
		// Get the update sites for current extension
		$query = $db->getQuery(true)
			->select($db->qn('update_site_id'))
			->from($db->qn('#__update_sites_extensions'))
			->where($db->qn('extension_id') . ' = ' . $db->q($extension_id));
		$db->setQuery($query);
		$updateSiteIDs = $db->loadColumn(0);
		if (!count($updateSiteIDs))
		{
			// No update sites defined. Create a new one.
			$newSite = (object) $update_site;
			$db->insertObject('#__update_sites', $newSite);
			$id = $db->insertid();
			$updateSiteExtension = (object) array(
				'update_site_id' => $id,
				'extension_id'   => $extension_id,
			);
			$db->insertObject('#__update_sites_extensions', $updateSiteExtension);
		}
		else
		{
			// Loop through all update sites
			foreach ($updateSiteIDs as $id)
			{
				$query = $db->getQuery(true)
					->select('*')
					->from($db->qn('#__update_sites'))
					->where($db->qn('update_site_id') . ' = ' . $db->q($id));
				$db->setQuery($query);
				$aSite = $db->loadObject();
				// Does the name and location match?
				if (($aSite->name == $update_site['name']) && ($aSite->location == $update_site['location']))
				{
					// Do we have the extra_query property (J 3.2+) and does it match?
					if (property_exists($aSite, 'extra_query'))
					{
						if ($aSite->extra_query == $update_site['extra_query'])
						{
							continue;
						}
					}
					else
					{
						// Joomla! 3.1 or earlier. Updates may or may not work.
						continue;
					}
				}
				$update_site['update_site_id'] = $id;
				$newSite = (object) $update_site;
				$db->updateObject('#__update_sites', $newSite, 'update_site_id', true);
			}
		}
	}
/**
 * Get extension Id
 *
 * @params void
 *
 * @return  extension id
 *
 * @since 1.1.7
 *
 */
public function getExtensionId()
{
	$db = $this->getDbo();
	// Get current extension ID
	$query = $db->getQuery(true)
		->select($db->qn('extension_id'))
		->from($db->qn('#__extensions'))
		->where($db->qn('type') . ' = ' . $db->q($this->extensionType))
		->where($db->qn('element') . ' = ' . $db->q($this->extensionElement));
	$db->setQuery($query);
	$extension_id = $db->loadResult();
	if (empty($extension_id))
	{
		return 0;
	}
	else
	{
		return $extension_id;
	}
}

Core Joomla Method

In case you want to use a pure Joomla Solution to do this,weeblr has contributed a quick tutorial to do this.
This method works from the following versions and up. The required changes in Joomla (adding the onInstallerBeforePackageDownload event) were introduced on those versions).

Joomla! 2: version 2.5.19+
Joomla! 3: version 3.2.3+

Joomla provides a simple and flexible mechanism for paid extensions to also update through the Joomla one-click updater. You have to create a plugin in the "Installer" group for your extension (or for multiple extensions) and you can then add the download id on the fly to the update site URL request (or pass credentials through request headers as we do).

The user can type in his/her download credentials in the plugin parameters if you want, or you can read it from the main component params.As a side bonus, it works the same way on Joomla 2.x and Joomla 3.x, so you can simply include the same plugin in both version of your extensions (if you have separate versions).

The downside is that you have to bundle an additional plugin of course. Here is a sample plugin doing this, which follows the URL format of ARS:

*
* @return boolean true if credentials have been added to request or not our business, false otherwise (credentials not set by user)
*
* @since 2.5
*/
public function onInstallerBeforePackageDownload(&$url, &$headers)
{
// are we trying to update our extension?
if (strpos($url, $this->baseUrl) !== 0)
{
return true;
}

// fetch download id from extension parameters, or
// wherever you want to store them
// Get the component information from the #__extensions table
JLoader::import('joomla.application.component.helper');
$component = JComponentHelper::getComponent($this->extension);

// assuming the download id provided by user is stored in component params
// under the "update_credentials_download_id" key
$downloadId = $component->params->get('update_credentials_download_id', '');

// bind credentials to request by appending it to the download url
if (!empty($downloadId))
{
$separator = strpos($url, '?') !== false ? '&' : '?';
$url .= $separator . 'dlid=' . $downloadId;
}
return true;
}
}

Related Posts

Leave Comments