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.
For the
radio
field type, includeclass="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.
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.
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 theClient ID
andClient 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
of700
. 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 thescriptfile 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 theinstall
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:
- The ZIP of the module package
- The XML for the auto-update capability
- 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.