A Labor of Love – Creating the LinkedIn Company Updates Module

In this post, I document the creation of the LinkedIn Company Updates module available on the Joomla Extensions Directory (JED).

The Need

LinkedIn company pages have a “Recent Updates” section that displays job postings, new products, and status updates. My client wanted to include this feed on their website. Exploring the Joomla Extensions Directory (JED), I was able to find several modules that integrated LinkedIn profile information, but not specifically company updates.

LinkedIn provides a robust REST API that allows a developer to retrieve company updates. So I set about creating a custom module. I had created custom modules before, but they were designed for specific functionality on a site-by-site basis. However, I had never gone through the process of packaging a module and making it available on the JED.

Reading the story of Basecamp, their product was born out of an internal necessity. That inspired me. I was going to put in the time anyway to create this LinkedIn module and I thought it may be useful to other Joomla developers, so I decided to put in the extra effort into making it a polished product and make it freely available to the open source community.

The Process

Joomla Documentation

First off, Joomla has pretty good documentation on how to create a custom module. It explains the basic directory structure and the critical files, the XML manifest file, how to add an auto-update capability, and how to add an install-uninstall-update script file. I followed this tutorial very closely and ended up with a solid foundation for the deeper dive into the customizations that were available.

Getting to Know Your Field Types

The display and layout of the module’s administrative interface is largely dictated by the XML manifest file. Part of that file is specifying the form fields. I was constantly referencing Joomla’s standard form field types documentation, using the text, note, hidden, number, spacer, and radio field types.

field-types

For the radio field type, include class="btn-group" as an attribute to give it the consistent Bootstrap look and feel.

<field
name="debug"
type="radio"
class="btn-group"
default="0"
label="MOD_LINKEDIN_DEBUG_LABEL"
description="MOD_LINKEDIN_DEBUG_DESC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>

The Authorization Endpoint displays a hyperlink that contains a carefully crafted URL based on the Client ID and Client Secret. This required custom PHP. To do this, I specified a custom name and type in the field XML.

<field
name="linkedincall"
type="linkedincall"
label="MOD_LINKEDIN_CALL_LABEL"
description="MOD_LINKEDIN_CALL_DESC" />

Now, I needed a PHP file called linkedincall.php within the module’s directory structure. To tell Joomla where to look for this file, I added the addfieldpath attribute to the fields tag and specified the absolute path to the file location as the value. For example:

<fields name="params" addfieldpath="/modules/mod_linkedin/fields">

In other words, the linkedincall.php file that generates the hyperlink is located in the /modules/mod_linkedin/fields directory. Now, all the programming logic is contained within that file.

LinkedIn

In order to use LinkedIn’s REST API, one must first authenticate with OAuth 2.0. LinkedIn has pretty good documentation on how to do this. It’s a multi-step process and in an attempt to simplify that process, I start with only displaying what is absolutely necessary for the administrator.

initial-fields

Hiding the other fields that were configured in the XML manifest file took a little bit of trickery. Within the logic of my linkedincall.php file, I check to see if the Client ID and Client Secret are already set as parameters. If they aren’t, I execute this little doozy:

$script = '
(function($){
$(document).ready(function() {
$(".span9 > .control-group:gt(1)").css("display", "none");
});
})(jQuery);';
$doc->addScriptDeclaration($script);

That convoluted selector hides all but the first 2 fields and then I inject that Javascript into the head of the page by using Joomla’s addScriptDeclaration API call. It may not be the best method to accomplish this, but it works rather nicely.

For security reasons, LinkedIn imposes a 20 second expiration on the Authorization Code. In other words, once an administrator goes through the process and generates the Authorization Code, if they don’t paste it into the Authorization Code field and Save the module within 20 seconds, they will have to click the Request Authorization Code hyperlink again. Not a big deal, but a pain in the you-know-where.

Armed with a valid Authorization Code, an Access Token is generated and stored in a hidden field that gets saved as a parameter. With a valid Access Token, I use LinkedIn’s REST API to get a company’s updates and then it’s a matter of parsing the beastly object that gets returned into useful HTML that gets displayed to the end-user.

Post-installation Messages

The Access Token provided by LinkedIn is valid for only 60 days. After 60 days, a new Access Token needs to be generated. As a way to remind an administrator of this 60 day expiration, I tapped into Joomla’s built-in Post-installation Message component. Joomla has pretty good documentation on how to configure post-installation messages.

The catch is that I didn’t want the post-installation message to display under the generic “Joomla” category. In the documentation, the generic “Joomla” category is referenced by an extension_id of 700. Instead, I wanted the post-installation message categorized specifically under “LinkedIn Company Updates”. To do this, I needed to get the extension ID that Joomla assigns the module after a successful installation. In the scriptfile install function, I got the extension ID with the following:

$extension_id = $parent->get('extension')->extension_id;

Then, instead of the generic 700 extension ID, I used the value returned from the parent class calling the install function.

No English!

No hard-coded English, that is. I made it a point to not hard-code any English throughout the module, whether it be field names, field descriptions, or additional instructions. That way, my module can be translated into any language. Joomla has pretty good documentation on the specification of language files, including the nifty _QQ_ trick to include double quotes in strings.

Further down in that documentation is a section for loading any language file, anywhere. I found this tidbit very useful for dynamically displaying additional instructions within my linkedincall.php file. For example:

$language = JFactory::getLanguage();
$language->load('mod_linkedin');
echo JText::_('MOD_LINKEDIN_DOCLINK_NOTE');

Submitting to the JED

Setting up the Environment on My Server

In order to have the module available for download within the JED, I needed an area within my server environment to host these 3 files:

  1. The ZIP of the module package
  2. The XML for the auto-update capability
  3. The XML for updating the JED remotely with new information

For me, that meant creating a “joomla” subdomain and creating a directory structure that not only made sense to me from an organizational standpoint, but also allowed for the flexibility of hosting additional extensions.

JEDChecker Component

So the first time I logged into the JED and submitted the module, I got some automated feedback about several issues with my module package. Admittedly, I can’t remember if the feedback mentioned the JEDChecker component or if I stumbled across it Googling, but it is a wonderful extension and very easy to use.

For my module in particular, it alerted me to some deprecated code I was using, to missing GPL License notices, and to missing JEXEC security. It also verified the information in my XML manifest file and it even ran an anti-malware scan. Once I corrected these issues, I re-submitted my module package and my LinkedIn Company Updates module was listed on the JED within a week.

Additional Resources